众所周知IOS的retain属性来解决了一些指针引用问题,但同时如果不了解其特点,也就很容易导致double free或没有free.
Student *st; //其中student为NSObject子类
@property (nonatomic,retain) Student *st;
再来看一下点操作。
self.st = xxx;
其实是执行了setter操作,展开原形为:
- (void)setSt:(Student *) ast
{
//请注意以下不能使 用self.操作
[st release]; //st 为全局成员变量
st = nil;
st = [ast retain];
}
如何清楚地认识自己的写法是否存在double free或存在泄漏.
习惯的几个写法:
-(void)viewDidLoad
{
[super viewDidLoad];
//写法一
st = [[Student alloc]init];
//写法二
self.st = [[[Student alloc]init]autorelease];
//写法三
Student *tmpst = [[Student alloc]init];
self.st = tmpst;
[tmpst release];
}
对应的dealloc写法
-(void) dealloc
{
[st release]; //或 self.st = nil;
[super dealloc];
}
分析:
对于方法一,没有使 用self.操作,即创建时retaincount = 1;
但这种全局成员,在类中使用时,如果时常用来作为左值被赋值时,要非常小心,看前面是否已实例化过了,如果没有则可以直接赋值,如果有了,见议使用self.操作。
举例(建立在上面例子的类中成员函数):
- (void) functionA
{
Student *otherst = [[Student alloc]init];
st = otherst;
[otherst release];
}
如果是初始化即(viewDidLoad)中进行将st进行了实例化。哪么有再次执行functionA,就会有内存泄漏,为什么?虽然在functionA中otherst释放了。此时的st 也指向了一个野指针的地址,哪么哪一块内存没有释放呢,就是在viewDidLoad 中 alloc出来的哪块地址被丢失了。当执行方法A后,没有任何实例能找到哪块内存,因此释放不了。为了避免泄漏,可以将方法A中的st = otherst ;改为self.st = otherst;为什么这样一改就好了呢?来看一下self.st = otherst ;做了什么,展开实际上是执行[self setSt:otherst]:
- (void)setSt:(Student *) ast
{
[st release]; //在这里先把在viewDidLoad 中alloc的哪块内存释放了,
st = nil; //并指向了NULL
st = [ast retain];//再将新的内存地址引用计数+1 ,即此时这里的st 就指向了 otherst的内存了。
}对方法一进行初始化的retain属性总结。只需要在初始化方法中进行alloc 一次,后续使用self.进行操作,可确保安全。
对于方法二的方法,我想看了方法一的分析应该很明白了,这种写法,其实是OC自动创建代码时的常用写法。简洁明了。整个类方法中除了自身set方法外,的其它地方都可以使 用self.来操作。
对于方法三,使用这种监时变量其实意义上是一样,只是看来麻烦,每次都搞个临时变量,增加代码上,不美观。
还有一个重点要注意的就是在内存告警的地方的写法:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
通常写法为
self.st = nil;//这样安全吗?还是使用[st release];安全。
}其实在这里安全是相对的.关键是清楚自己alloc出来的内存,什么时候release,过程中有没有release.