非ARC下的开发-----retain、assign的使用详解

来源:互联网 发布:人工智能人才缺口 编辑:程序博客网 时间:2024/05/17 02:55

非ARC的内存管理原则
1、如果调用allock/new/copy产生一个新对象时必须要有调用一次release或autorelease
2、如果让一个对象进行了一次retain操作,最后肯定要调用一次release或autorelease操作
3、原则是计数器有加就有有减

assign一般用于简单的数据类型(如如NSInteger,double,bool)、代理等,retain一般用于对象

下面是通过代码进行一步步分析
问题代码一
Person.h文件

#import <Foundation/Foundation.h>@class Dog;@interface Person : NSObject{    Dog *_dog;};- (void)setDog:(Dog *)dog;- (Dog *)dog;@end

Person.m文件

#import "Person.h"#import "Dog.h"@implementation Person- (void)setDog:(Dog *)dog{    _dog = dog;}- (Dog *)dog{    return _dog;}@end

以上的代码相当于@property(assign ,nonatomic)Dog *dog;
main.m文件

int main(int argc, char * argv[]) {    @autoreleasepool {        Person *person = [[Person alloc]init];//1        Dog *dog1 = [[Dog alloc]init];//1        person.dog = dog1;//        [dog1 release];//dog1:0        NSLog(@"%@",person.dog);        [person release];    }}
在打印前dog1已经释放,此时,_dog变成了僵尸指针。结果显示消息发送给了一个释放的对象![](http://img.blog.csdn.net/20160630212507264)Xcode开启僵尸模式的方法:在Xcode的scheme页面中设置NSZombieEnabled环境变量。点击Product——>Edit Scheme打开该页面,然后勾选Enable Zombie Objects 复选框。

修改后的代码一(依然有问题)

#import "Person.h"#import "Dog.h"@implementation Person- (void)setDog:(Dog *)dog{    _dog = [dog retain];}- (void)dealloc{    [self.dog release];    [super dealloc];}- (Dog *)dog{    return _dog;}@end

修改了Person.m文件内容,修复了代码一中的问题
存在的问题
main.m文件

int main(int argc, char * argv[]) {    @autoreleasepool {        Person *person = [[Person alloc]init];//1        Dog *dog1 = [[Dog alloc]init];//1        Dog *dog2 = [[Dog alloc]init];        person.dog = dog1;//dog1:2        person.dog = dog2;//dog2:2        [dog1 release];//dog1:1        [dog2 release];//dog2:1        [person release];//person:0 dog2:0    }}

从运行结果可以看出三个对象只有两个释放了
修改Persom.m的代码

- (void)setDog:(Dog *)dog{    [_dog release];    _dog = [dog retain];}

在retain新值前先把旧值release掉
运行结果:
三个对象都被释放了

该代码依然有问题
main.m文件代码

int main(int argc, char * argv[]) {    @autoreleasepool {        Person *person = [[Person alloc]init];//1        Dog *dog1 = [[Dog alloc]init];//1        person.dog = dog1;//dog1:2        [dog1 release];//dog1:1        person.dog = dog1;//dog1:0        [person release];//person:0    }}

第一次person.dog = dog1前person.dog =nil,release旧值相当于[nil release],dog1的计数器不变还是1,又进行了一次retain操作,所以dog1的计数器是2
第二次person.dog = dog1之前有一次release操作,dog1计数器变为1。此时,再person.dog = dog1时因为person.dog有旧值,先进行release操作,dog1计数器此时为为0,dog1被释放。再进行retain操作就会有野指针错误。

修改后的正确的代码

- (void)setDog:(Dog *)dog{    if (_dog!=dog) {        [_dog release];        _dog = [dog retain];    }}

此时的代码相当于@property(retain ,nonatomic)Dog *dog;

比较严谨的写法还要修改-(void)dealloc方法

- (void)dealloc{    [_dog release];    _dog = nil;    [super dealloc];}

或者

- (void)dealloc{    self.dog = nil;    [super dealloc];}

注意iOS版本是5.0或4.3时在控制器使用retain关键字的属性时还要添加以下代码

- (void)viewDidUnload{    [super viewDidUnload];    self.dog = nil;}- (void)didReceiveMemoryWarning {    [super didReceiveMemoryWarning];    self.dog = nil;}
0 0
原创粉丝点击