web-dev-qa-db-fra.com

Comment initialiser un NSMutableArray dans Objective C?

Je viens d'un contexte Java et j'apprends Objective C. J'essaie de créer une classe qui possède un tableau de chaînes et une fonction membre pour modifier le tableau. Mon code ressemblait à ceci:

@implementation TAWChapter

@synthesize mSubject;

@synthesize mItems;

- (id) init{
    self.mItems = [[NSMutableArray alloc] init];
    return self; 
}

- (void) setSubject:(NSString *)subject{
    self.mSubject = subject; 
}

- (void) addItem:(NSString *)item{
    [self.mItems addObject:@"asdf"]; 
}

@end

Ce qui n'a pas fonctionné. J'ai un "[__NSArrayI addObject:]: unrecognized selector sent to instance " et un "NSInvalidArgumentException". Après une recherche sur Internet, j'ai modifié la ligne unique du constructeur en:

self.mItems = [self.mItems init];

Cela a fonctionné, mais pourquoi? Du point de vue du développeur Java, le premier a plus de sens que le second. Et j'ai une autre ligne, c'est la même chose que la première mais ça marche (pas dans un constructeur). Est ce que quelqu'un pourrait m'expliquer ça, s'il vous plait?

11
Xi Zhang
  1. Tout d’abord, respectez les conventions de codage Objective-C. En Objective-C, les classes n'ont pas de constructeur, elles ont des initialiseurs. En Objective-C, les initialiseurs invoquent l'initialiseur de la super-classe, il devrait donc ressembler à ceci:

    - init
    {
        self = [super init];
        if (!self) return nil;
    
        // set up other stuff here 
    
        return self;
    }
    
  2. Deuxièmement, à moins d'utiliser ARC, vous risquez d'avoir une fuite de mémoire. La première ligne de votre initialiseur affecte un objet que vous possédez à une propriété dont il est également probable qu'il en devient propriétaire. Vous devriez utiliser soit:

    // property takes care of ownership
    self.mItems = [NSMutableArray array];
    

    ou:

    // assign to instance variable directly with owned object
    mItems = [[NSMutableArray alloc] init];
    

    Apple décourage parfois l'utilisation de méthodes d'accès dans les initialiseurs, car elle peut manipuler des éléments tels que KVO, mais une utilisation cohérente des méthodes d'accès garantit la propriété appropriée des objets et la gestion de la mémoire.

  3. En changeant votre ligne dans votre initialiseur pour:

    self.mItems = [self.mItems init];
    

    fait rien . Lors de l'appel de votre méthode d'initialisation (généralement juste après son affectation), toutes les variables d'instance sont automatiquement définies sur nil. Donc, ce que vous faites est juste:

    self.mItems = [nil init];
    

    qui est juste:

    self.mItems = nil;
    

    et, n'utilisez pas init sans allouer au préalable une instance, et n'utilisez jamais init plus d'une fois.

  4. Si vous ne laissez pas la super-classe s'initialiser elle-même, cela peut se traduire par des problèmes dans d'autres domaines. Faites un Build & Analyze et assurez-vous d’avoir corrigé tout problème signalé par l’analyseur.

28
dreamlax

Puisque l’objectif-c est un sur-ensemble de c, c’est fondamentalement c avec du sucre de syntaxe "OO". Avant de pouvoir créer (ou utiliser!) Un objet, vous devez lui allouer de l'espace dans le tas. vous faites cela avec [Class alloc]. La prochaine étape est l'initialisation de cet espace. alloc retourne un pointeur sur cet espace du tas, que vous initialisez avec init;) 

Donc, vous appelez Class *myObjc = [[Class alloc] init];.

Si vous utilisez l'héritage (ce que vous faites depuis que vous héritez de NSOBject), vous devez vous assurer que votre super-classe a tout initialisé correctement, c'est l'appel à super. Pour vous assurer que vous n'obtenez pas d'erreur d'exécution, vérifiez self! = Nil, ce que vous faites implicitement avec if (self)

self.mItems = [self.mItems init]; // doesn't do anything, since you call the getter for mItems with self.mItems and try to init. then you try to set mItmes to itself. 

utilisez ce code:

@implementation TAWChapter

@synthesize mSubject, mItems;

- (id)init
{
    self = [super init];
    if (self) {
        mItems = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void) setSubject:(NSString *)subject{
    mSubject = subject; 
}

- (void) addItem:(NSString *)item{
    [mItems addObject:item]; 
}

@end
6
MJB

Vous devez appeler super et attribuer son résultat à self dans votre méthode init:

- (id)init
{
    self = [super init];
    if (self) {
        self.mItems = [[NSMutableArray alloc] init];
    }
    return self;
}
3
omz

L’autre solution pourrait être de créer NSMutableArray à partir de NSArray:

NSMutableArray *myMutableArray = [NSMutableArray arrayWithArray:myArray];
0
Darius Miliauskas