web-dev-qa-db-fra.com

Variables d'instance pour les catégories C Objectif C

J'ai une situation où il semble que je dois ajouter des variables d'instance à une catégorie, mais je sais des documents d'Apple que je ne peux pas faire cela. Je me demande donc quelle est la meilleure alternative ou la meilleure solution de contournement.

Ce que je veux faire, c'est ajouter une catégorie qui ajoute des fonctionnalités à UIViewControlers. Je le trouverais utile dans tous mes différents UIViewControlers, peu importe la sous-classe spécifique UIViewController, donc je pense qu'une catégorie est la meilleure solution. Pour mettre en œuvre cette fonctionnalité, j'ai besoin de plusieurs méthodes différentes et j'ai besoin de suivre les données entre eux, c'est ce qui me conduit à vouloir créer des méthodes d'instance.

Au cas où il sera utile, voici ce que je veux spécifiquement faire. Je tiens à faciliter la suivre lorsque le clavier du logiciel se cache et montre, de sorte que je puisse redimensionner le contenu à mon avis. J'ai constaté que le seul moyen de le faire de manière fiable est de mettre le code dans quatre méthodes d'UIViewController différentes et de suivre des données supplémentaires par exemple des variables. Donc, ces méthodes et variables d'instance sont ce que j'aimerais mettre dans une catégorie, donc je n'ai pas à les copier-les coller chaque fois que je dois gérer le clavier du logiciel. (S'il y a une solution plus simple pour ce problème exact, c'est bien aussi - mais j'aimerais toujours connaître la réponse aux variables d'instance de catégorie pour référence future!)

32
Josh Justice

Oui, vous pouvez le faire, mais comme vous demandez, je dois demander: êtes-vous absolument sûr que vous devez? (Si vous dites "oui", revenez en arrière, découvrez ce que vous voulez faire et voyez s'il y a une façon différente de le faire)

Toutefois, si vous souhaitez vraiment injecter le stockage dans une classe, vous ne contrôlez pas, utilisez une référence associative .

28
Dave DeLong

Récemment, je devais faire cela (ajouter un État à une catégorie). @Dave Delong a la bonne perspective à ce sujet. Dans la recherche de la meilleure approche, j'ai trouvé un excellent Blog Post de Tom Harrington. J'aime l'idée de @ Jeremyp d'utiliser des déclarations de @property sur la catégorie, mais pas sa mise en œuvre particulière (pas un fan du Singleton mondial ou des références globales). Les références associatives sont la voie à suivre.

Voici le code pour ajouter (ce qui semble être) ivars à votre catégorie. J'ai blogué à propos de cela en détail ici .

Dans File.h, l'appelant ne voit que l'abstraction propre et de haut niveau:

@interface UIViewController (MyCategory)
@property (retain,nonatomic) NSUInteger someObject;
@end

Dans File.M, nous pouvons implémenter le @Property (Remarque: Celles-ci ne peuvent pas être @ synthétisé'd):

@implementation UIViewController (MyCategory)

- (NSUInteger)someObject
{
  return [MyCategoryIVars fetch:self].someObject;
}

- (void)setSomeObject:(NSUInteger)obj
{
  [MyCategoryIVars fetch:self].someObject = obj;
}

Nous devons également déclarer et définir la classe myCateGoryivars. Pour la facilité de compréhension, j'en ai expliqué cette commande de compilation appropriée. Le @interface doit être placé avant la catégorie @Implementatation.

@interface MyCategoryIVars : NSObject
@property (retain,nonatomic) NSUInteger someObject;
+ (MyCategoryIVars*)fetch:(id)targetInstance;
@end

@implementation MyCategoryIVars

@synthesize someObject;

+ (MyCategoryIVars*)fetch:(id)targetInstance
{
  static void *compactFetchIVarKey = &compactFetchIVarKey;
  MyCategoryIVars *ivars = objc_getAssociatedObject(targetInstance, &compactFetchIVarKey);
  if (ivars == nil) {
    ivars = [[MyCategoryIVars alloc] init];
    objc_setAssociatedObject(targetInstance, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [ivars release];
  } 
  return ivars;
}

- (id)init
{
  self = [super init];
  return self;
}

- (void)dealloc
{
  self.someObject = nil;
  [super dealloc];
}

@end

Le code ci-dessus déclare et met en œuvre la classe qui détient nos iTARD (ToOObject). Comme nous ne pouvons pas vraiment prolonger UIViewController, cela devra faire.

14
Andrew Philips

Je crois qu'il est maintenant possible d'ajouter des propriétés synthétisées à une catégorie et des variables d'instance sont créées automatiquement, mais je ne l'ai jamais essayée, donc je ne suis pas sûr que cela fonctionne.

Une solution plus hacky:

Créez un Nsdictionnel Singleton qui aura l'UIViewController comme clé (ou plutôt son adresse enveloppée comme une NSValue) et la valeur de votre propriété comme valeur.

Créez Getter and Setter pour la propriété qui va réellement au dictionnaire pour obtenir/définir la propriété.

@interface UIViewController(MyProperty)

@property (nonatomic, retain) id myProperty;
@property (nonatomic, readonly, retain) NSMutableDcitionary* propertyDictionary;

@end

@implementation  UIViewController(MyProperty)

-(NSMutableDictionary*) propertyDictionary
{
    static NSMutableDictionary* theDictionary = nil;
    if (theDictionary == nil)
    {
        theDictioanry = [[NSMutableDictionary alloc] init];
    }
    return theDictionary;
}


-(id) myProperty
{
    NSValue* key = [NSValue valueWithPointer: self];
    return [[self propertyDictionary] objectForKey: key];
}

-(void) setMyProperty: (id) newValue
{
    NSValue* key = [NSValue valueWithPointer: self];
    [[self propertyDictionary] setObject: newValue forKey: key];    
}

@end

Deux problèmes potentiels avec l'approche ci-dessus:

  • il n'y a aucun moyen de supprimer les clés des contrôleurs de visualisation qui ont été distribués. Tant que vous ne suivez qu'une poignée, cela ne devrait pas être un problème. Ou vous pouvez ajouter une méthode pour supprimer une clé à partir du dictionnaire une fois que vous savez que vous avez terminé avec elle.
  • Je ne suis pas certain que l'ISEAR: la méthode de NSValue compare le contenu (c'est-à-dire le pointeur enveloppé) pour déterminer l'égalité ou s'il se compare simplement à voir si l'objet de comparaison est exactement le même NSValue. Si ce dernier, vous devrez utiliser NSNumber au lieu de NSValue pour les clés (NSNumber numberWithUnsignedLong: fera l'affaire sur les plates-formes 32 bits et 64 bits).
6
JeremyP

Ceci est mieux atteint à l'aide des objets associés à une fonction OBJC intégrée (AKA associé des références), dans l'exemple ci-dessous, passez simplement à votre catégorie et remplacez-le associé à votre nom de variable.

nsObject + associéObject.h

@interface NSObject (AssociatedObject)
@property (nonatomic, strong) id associatedObject;
@end

nsobject + associéObject.m

#import <objc/runtime.h>

@implementation NSObject (AssociatedObject)
@dynamic associatedObject;

- (void)setAssociatedObject:(id)object {
     objc_setAssociatedObject(self, @selector(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)associatedObject {
    return objc_getAssociatedObject(self, @selector(associatedObject));
}

Voir ici pour le tutoriel complet:

http://nshipster.com/associated-Objects/

6
malhal

Il a mentionné dans de nombreux documents en ligne que vous ne pouvez pas créer créer une nouvelle variable dans la catégorie, mais j'ai trouvé un moyen très simple d'y parvenir. Voici la façon qui laissent déclarer une nouvelle variable dans la catégorie.

Dans votre fichier .h

@interface UIButton (Default)

   @property(nonatomic) UIColor *borderColor;

@end  

Dans votre fichier .m

#import <objc/runtime.h>
static char borderColorKey;

@implementation UIButton (Default)

- (UIColor *)borderColor
{
    return objc_getAssociatedObject(self, &borderColorKey);
}

- (void)setBorderColor:(UIColor *)borderColor
{
    objc_setAssociatedObject(self, &borderColorKey,
                             borderColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    self.layer.borderColor=borderColor.CGColor;
}

@end

C'est maintenant que vous avez la nouvelle variable.

3
Varun Naharia

Selon ce que vous faites, vous voudrez peut-être utiliser des méthodes de catégorie statiques.

Donc, je suppose que vous avez ce genre de problème:

ScrollView a quelques textes en eux. Types d'utilisateurs sur texte Modifier, vous souhaitez faire défiler la vue Défilement afin que le texte modifier est visible au-dessus du clavier.

+ (void) staticScrollView: (ScrollView*)sv scrollsTo:(id)someView
{
  // scroll view to someviews's position or some such.
}

le retour de cela ne nécessiterait pas nécessairement la vue de revenir, et il n'a donc pas besoin de stocker quoi que ce soit.

Mais c'est tout ce que je peux penser sans exemples de code, désolé.

0
Stephen Furlani

Je pense qu'il est possible d'ajouter des variables à une classe à l'aide de l'exécution OBJ-C.
[.____] J'ai trouvé cette discussion aussi.

0
Richard