- 属性的语义特性以及使用范围:
assign
retain
copy - assign下的属性内部实现:
@property (nonatomic, assign) NSString *name;
#pragma mark setter方法
- (void)setName:(NSString *)name
{
_name = name; }
#pragma mark getter方法
- (NSString *)name
{
return _name; }
// 对于上面的实现,很容易产生内存问题,比如:
NSString *name = [[NSString alloc] initWithFormat:@"小黑"];
Person *person = [[Person alloc] init];
[person setName:name];
[name release];
NSLog(@"person.name = %@", [person name]); // 此处会出现野指针异常
[person release];
// 在内存被系统回收之后,还在使用。assign适合基本数据类型,基本数据类型不会引起内存问题。
- retain下的属性内部实现:
#pragma mark setter方法
- (void)setName:(NSString *)name
{
if (_name != name) { [_name release]; _name = [name retain]; } }
#pragma mark getter方法
- (NSString *)name
{
return [[_name retain] autorelease]; }
// Main.m中
NSString *name = [[NSString alloc] initWithFormat:@"小绿"]; // name引用计数为1
Person *p = [[Person alloc] init]; // p应用技数为1
[p setName:name]; // name引用计数为2,setName:内部执行了retain方法
[name release]; // name引用计数为1
NSLog(@"%@", [p name]);
NSString *newName = [[NSString alloc] initWithFormat:@"小白"]; // newName引用计数为1
[p setName:newName]; // name引用计数为0,newName引用计数为2
[newName release]; // newName引用计数为1
NSLog(@"%@", [p name]);
[p release]; // p引用计数为1
// 上面两处NSLog(@"%@", [p name]); 都不会引起野指针异常。每次[p setName:xx]
都会先释放旧的对象,retain新的对象,即:name引用计数先减1,然后newName引用计数加1。上面的代码执行完毕后,name引用计数为0,然后anotherName引用计数加1。上面的代码执行完毕后,name引用计数为0,name所占的内存被系统回收了,_name引用计数为1,仍然占有内存,p引用计数为0,p所占的内存被系统回收。(代码执行完毕时_name占有的内存没有释放,引起了内存泄露)苹果为了保证程序的安全,retain对应的getter方法是先retain后autorelease。
// retain适用于对象类型的属性
- copy下的属性内部实现:
#pragma mark setter方法
- (void)setName:(NSString *)name
{
if (_name != name) { [_name release]; _name = [name copy]; } }
#pragma mark getter方法
- (NSString *)name
{
return [[_name retain] autorelease]; }
// main.m文件
NSString *name = [[NSString alloc] initWithFormat:@"张三"]; // name引用计数为1
Person *p = [[Person alloc] init]; // p引用计数为1
[p setName:name]; // name引用计数为1, _name引用计数为1, setName:内部name执行了copy方法
[name release]; // name引用计数为0, name占有的内存被释放
NSLog(@"%@", [p name]); // 此处打印的是_name
NSString *newName = [[NSString alloc] initWithFormat:@"小华"]; // newName引用计数为1
[p setName:newName]; // _name先是引用计数为0,newName引用计数为1,_name引用计数为1,因为setName:内部[newName copy];
[newName release]; // newName引用计数为0
NSLog(@"%@", [p name]); // 打印的是_name
[p release]; // p引用计数为0
// 上面的两处NSLog(@"%@", [p name]); 都不会引起野指针异常。每次[p setName:xx];都会先释放旧的对象,copy新的对象,即:_name引用计数为先减1,变为0,然后_name = [newName copy], _name引用计数变为1;下次如果给p设置新的名字anotherName,_name引用计数先减1,变为0,然后_name = [anotherName copy], _name引用计数为1。上面的代码执行完毕后,name应用技术为0,name所占内存被系统回收了,newName引用计数为0,所占的内存被系统回收,p引用计数为0,p所占的内存被系统回收。_name引用计数为1,占用着内存,不能得到释放。(代码执行完毕时_name占有的内存没有释放,引起了内存泄露)苹果为了保证程序的安全,retain对应的getter方法先是retain后autorelease。
// copy适用于接受了NSCopying协议的对象,因为只有接受了NSCopying协议,对象才能执行copy操作
- 什么是dealloc?
dealloc是NSObject的一个实例方法,与alloc对应用于回收开辟的内存空间
这个方法在对象引用计数为0时,由系统自动调用
通常我们在dealloc中释放类的实例变量 - 如何使用dealloc?
// Person.m中
- (void)dealloc
{
[_name release]; // 释放setter方法泄露的 [super dealloc]; }
- dealloc注意事项:
永远不要手动调用dealloc
在dealloc方法的最后一行,必须要写[super dealloc];
- Person类的便利构造器:
// Person.m中
+ (id)personWithName:(NSString *) name
{
Person *p = [[Person alloc] initWithName:name]; return p; }
// 上面我们初始化的p对象,我们alloc之后,要在什么时候释放呢????
// 正确写法
+ (id)personWithName:(NSString *) name
{
Person *p = [[Person alloc] initWithName:name]; return [p autorelease]; }
- return
[p autorelease];是最完美的解决方案,既不会内存泄露,也不会产生野指针。
- collection:
collection就是NSArray,NSDictionary,NSSet...等容器类
collection会自主管理自己内部的元素 - 加入collection中的对象会被retain
- 移除出collection的对象会被release
- collection被释放会对内部所有对象release