rob*_*408 1 iphone cocoa memory-management objective-c
我有一个对象,我像正常一样分配/初始化以获取实例.稍后在我的应用程序中,我想从磁盘为该对象加载状态.我想我可以解压缩我的类(符合NSCoding)并只交换我的实例所指向的位置.为此,我使用此代码...
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];
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当我在整个应用程序中撒了一些NSLog时,我注意到了
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)
Run Code Online (Sandbox Code Playgroud)
我错过了什么?
不要这样做.除了它打破了身份规则,你不能改变程序其他部分持有的指针值.
更好的方法是使用PIMPL习语:您的类包含指向实现对象的指针,您只需交换它.
像这样的东西:
@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
Run Code Online (Sandbox Code Playgroud)
self
是实例方法的函数参数.分配给self是完全合理的,就像为其他函数参数赋值是完全合理的.由于范围self
是当前函数,因此您的代码会泄漏一个对象并以最可能导致崩溃的方式释放另一个对象.
分配给它的唯一有意义的self
是在init
方法中.虽然它几乎从不使用,init
但允许方法释放self并分配一个新对象返回或只返回nil.这种方法的唯一原因是因为返回值是self,并且调用者init
期望使用返回值.
正如gf指出的那样,正确的方法是一个加载函数,它将新值分配给实例的成员,而不是尝试替换实例.