2010-05-10 7 views
1

J'ai un objet que j'all/init comme normal juste pour obtenir une instance. Plus tard dans mon application, je veux charger l'état du disque pour cet objet. Je pense que je pourrais désarchiver ma classe (ce qui est conforme à NSCoding) et juste échanger où mon instance pointe vers. A cette fin, j'utilise ce code ...Changer le pointeur de soi

NSString* pathForDataFile = [self pathForDataFile]; 
if([[NSFileManager defaultManager] fileExistsAtPath:pathForDataFile] == YES) 
{ 
    NSLog(@"Save file exists"); 
    NSData *data = [[NSMutableData alloc] initWithContentsOfFile:pathForDataFile]; 
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; 
    [data release]; 

    Person *tempPerson = [unarchiver decodeObjectForKey:@"Person"]; 
    [unarchiver finishDecoding]; 
    [unarchiver release]; 

    if (tempPerson) 
    { 
     [self release]; 
     self = [tempPerson retain]; 
    } 
} 

Maintenant, quand je saupoudré quelques NSLogs tout au long de ma demande, j'ai remarqué

self.person: <Person: 0x3d01a10> (After I create the object with alloc/init) 
self: <Person: 0x3d01a10> (At the start of this method) 
tempPerson: <Person: 0x3b1b880> (When I create the tempPerson) 
self: <Person: 0x3b1b880> (after i point self to the location of the tempPerson) 
self.person: <Person: 0x3d01a10> (After the method back in the main program) 

Qu'est-ce que je manque?

Répondre

5

self est un argument de fonction à des méthodes d'instance. Assigner à soi est parfaitement raisonnable, tout comme assigner des valeurs à d'autres arguments de fonction est parfaitement raisonnable. Puisque la portée de self est la fonction actuelle, votre code fuit un objet et en libère un autre d'une manière qui causera probablement un crash.

Le seul moment où il est significatif pour affecter à self se situe dans une méthode init. Bien qu'il ne soit presque jamais utilisé, les méthodes init sont autorisées à libérer le self et à allouer un nouvel objet à renvoyer ou juste à renvoyer nul. La seule raison pour laquelle cela fonctionne est parce que la valeur de retour est auto et les appelants init s'attendent à utiliser la valeur de retour.

Comme gf l'a souligné, l'approche correcte est une fonction de chargement qui affecte de nouvelles valeurs aux membres de votre instance, et non à ceux qui tentent de remplacer l'instance.

+0

Les deux excellentes réponses. Celui-ci me donne un peu plus d'informations sur les raisons pour lesquelles mon approche était si mauvaise, merci! – rob5408

6

Ne faites pas cela. En plus de cela, il casse les règles d'identité, vous ne pouvez pas modifier les valeurs de pointeur des autres parties d'un programme.

Une meilleure approche serait d'utiliser l'idiome PIMPL: votre classe contient un pointeur vers un objet d'implémentation et vous ne faites qu'échanger cela.

E.g. quelque chose le long des lignes de celle-ci:

@class FooImpl; 
@interface Foo { 
    FooImpl* impl; 
} 
// ... 
- (void)load; 
@end 

@implementation Foo 
- (void)load { 
    FooImpl* tmp = loadFromDisk(); 
    if (tmp) { 
     FooImpl* old = impl; 
     impl = tmp; 
     [old release]; 
    } 
} 
@end 
+0

Merci! Je vais donner un coup de feu. – rob5408