2011-07-30 3 views
0

Je suis nouveau à Objective-C. J'essaie d'obtenir une propriété définie dans le constructeur, mais j'obtiens une erreur EXC_BAD_ACCESS. Voici mon constructeur:Obtention de propriétés définies dans le constructeur

- (id) init { 
    self = [super init]; 
if (self != nil) { 
    appFolderPath = [[NSBundle mainBundle] resourcePath]; 
    fileManager = [NSFileManager defaultManager]; 
    mediaArray = [fileManager directoryContentsAtPath: [appFolderPath stringByAppendingString:@"/Media/Silly"]];  
    mediaIndex = 0; 
} 
return self; 

}

Voici mes propriétés:

@property (retain) NSFileManager* fileManager; 
@property (retain) NSString* appFolderPath; 
@property int mediaIndex; 
@property (retain) NSArray* mediaArray; 

Des idées?

+0

Je signale que '-directoryContentsAtPath:' est obsolète, utilisez '- (NSArray *) contentsOfDirectoryAtPath: (NSString *) erreur de chemin: (NSError **) error' à la place. –

Répondre

3

Vous avez le mot-clé retain sur vos propriétés, ce qui est bon. Mais, cela n'a pas d'importance parce que vous ne les utilisez pas réellement. Vous accédez directement aux ivars, en ignorant la méthode getter générée par le compilateur Objective-c.

Pour contraste entre et Ivar et une propriété, voir ce code:

MyInterface.h

@interface MyInterface : NSObject { 
@private 
    NSFileManager * fileManager; // This is an instance variable, or 'ivar' 
} 

@property (retain) NSFileManager * fileManager; // This is the declaration of a property 

MyInterface.m

@implementation MyInterface { 

@synthesize fileManager; 

// Calling this causes the Objective-C compiler to generate the following 
// methods for you: 
// -(NSFileManager *) getFileManager ... 
// and - (void) setFileManager: (NSFileManager *) val ... 
} 

Pour utiliser la Ivar de votre classe, vous feriez faites simplement référence à son nom, dans votre exemple vous l'avez fait avec la ligne:

fileManager = [NSFileManager defaultManager]; 

Étant donné que l'instance libérée automatiquement renvoyée par la méthode que vous avez appelée n'est pas conservée, vous obtenez une exception EXEC_BAD_ACCESS plus tard dans votre programme. Pour utiliser la propriété que vous devez préfacer avec la référence d'objet possédant:

self.fileManager = [NSFileManager defaultManager]; 

Cela garantit que votre Ivar est réglé et conservé.


EDIT

Maintenant, pour apprécier vraiment la différence entre une variable d'instance et une propriété, vous pourriez avoir déclaré votre interface comme:

@interface MyInterface : NSObject { 
@private 
    NSFileManager * _fileManager; 
} 

@property (retain) NSFileManager * fileManager; 

Et dans votre .m vous aurait:

@synthesize fileManager = _fileManager.

Maintenant, lorsque vous avez essayé de faire fileManager = [NSFileManager defaultManager]; votre code ne compilerait pas, car il n'y a pas d'ivar appelé fileManager dans votre classe. C'est un style de codage utile pour éviter les erreurs courantes.

+0

La méthode préférée pour les variables d'instance consiste à ajouter un trait de soulignement en tant que suffixe, pas en tant que préfixe. La raison en est que si vous sous-classes des classes d'Apple, vous rencontrez le problème potentiel de marcher sur une variable d'instance privée du super qui peut entraîner des résultats imprévisibles: 'NSFileManager * fileManager_;' –

+0

Ok. Donc, si je crée mes variables d'instance comme "nom_" et mes propriétés comme "nom", quand aurais-je accès à la propriété sur la variable d'instance? – user472292

0

Vous n'attribuez pas à propriétés, vous affectez à les variables d'instance. Si vous souhaitez utiliser les propriétés, vous devez préfixer les noms avec self., comme dans self.fileManager.

L'effet est que les objets ne sont pas conservés, et seront probablement désaffectés plus tard.Lorsque vous essayez ensuite d'accéder aux propriétés (ou à leurs variables d'instance), les objets sont déjà supprimés. Parfois, il y a un objet différent à leur place, parfois c'est une poubelle. Et c'est quand vous obtenez le crash.

1

@Perception a déjà expliqué les tenants et aboutissants de pourquoi cela ne fonctionne pas. Voici comment rectifier cela. Puisque vous n'utilisez pas les setters synthétisés qui enverraient le message de conservation, vous êtes obligé de le gérer vous-même lorsque vous accédez directement aux variables d'instance. Notez également que les objets de type NSString doivent être copiés au lieu d'être conservés, de sorte que la déclaration de appFolderPath doit effectivement être @property (nonatomic, copy) NSString *appFolderPath;. Compte tenu de tout cela, votre -init devrait ressembler à ceci.

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

    if (self != nil) 
    { 
     appFolderPath = [[NSBundle mainBundle] resourcePath] copy]; 
     fileManager = [NSFileManager defaultManager] retain]; 
     mediaArray = [fileManager directoryContentsAtPath:[appFolderPath stringByAppendingString:@"/Media/Silly"]] retain];  
     mediaIndex = 0; 
    } 

    return self; 
} 
0

Je ne veux pas marcher sur les orteils - @Perception a fourni une explication approfondie et a donné la solution @ Mark réelle.

voici la version rapide:

Ce

@property (nonatomic, copy) NSString* appFolderPath; 

et correspondant à

@synthesize appFolderPath = _appFolderPath; 

signifie que le compilateur génère les méthodes suivantes

- (void)setAppFolderPath(NSString *)appFolderPath; 
- (NSString *)appFolderPath; 

Ces meth ods prendra en charge la gestion de la mémoire de conserver/copier/référencer un objet sur l'affectation en fonction de l'option que vous choisissez retain/copy/assign. Cette gestion de la mémoire n'entrera en vigueur si vous utilisez

[self setAppFolderPath:@"My Folder Path"]; 

ou

self.appFolderPath = @"My Folder Path"; // which compiles to the previous call 

Maintenant, tout cela est bien et bon pour un usage général dans votre class mais parfois il est recommandé de ne pas utiliser accesseurs dans le cas où ils provoquent des effets secondaires. Deux endroits où vous voulez normalement éviter les getters et les setters sont dans les méthodes init et dealloc. Par conséquent, comme vous étiez dans la méthode init, vous devez accéder à l'ivar directement sans utiliser les getters/setters. Cela signifie que vous devrez effectuer vous-même la gestion de la mémoire. par exemple.

- (id)init 
{ 
    self = [super init]; 
    if (self) { 
     _appFolderPath = [[[NSBundle mainBundle] resourcePath] copy]; 
    } 
    return self; 
} 

Dans la méthode dealloc vous accéderaient au Ivar directement à nouveau comme celui-ci

- (void)dealloc 
{ 
    [_appFolderPath release]; 
    [super dealloc]; 
} 

Comme vous fait remarquer @ Mark généralement copy chaînes afin qu'ils ne peuvent pas être modifiés ci-dessous vous.

Notes supplémentaires sur les exemples.

Lorsque j'ai appelé @synthesize appFolderPath = _appFolderPath; Il crée les getters et les setters en utilisant le nom appFolderPath mais l'effet ivar ces méthodes est actuellement appelé _appFolderPath. C'est une bonne pratique car vous serez toujours sûr lorsque vous accédez à l'ivar directement ou par l'intermédiaire d'un getter/setter parce que le fait de simplement référencer appFolderPath dans votre code ne compilera pas.

Questions connexes