Objective-C内存管理之引用计数
2016-01-07 15:48
549 查看
Apple在iOS5之后引入了自动ARC技术,让编译器来自动来管理内存,开发者从此不用再去写很多retain、release代码了。ARC下,编译器完全清楚目标对象,并且能够自动释放那些需要释放的对象,因此,程序内存泄漏的风险大大降低了,但!ARC!不!是!万!能!的!
在实际开发中,内存泄漏还是比较常见的,比如使用block时候的不注意,导致的循环引用;比如在使用NSTimer实例的时候,timer的action事件执行者也就是Target会强引用Self,如果timer没有正确的销毁,就会造成内存泄漏等等;这个时候我们就需要来手动管理内存了。因此,很有必要知道Objective -C的内存管理机制。
二、Objective-C内存管理是机制,就是引用计数。
正确客观的思考方式:
1)自己生成的对象,自己持有。
2)非自己生成的对象,自己也能持有。
3)自己持有的对象,一旦不再需要,就需要立即释放。
4)非自己持有的对象,自己无法释放。
这里的“自己”,知道是当前的对象的使用环境,也可以理解为程序猿自己啦。
问题一:
什么情况下自己生成的对象,自己持有?
使用以下名称开头的方法名,意味着自己生成的对象只有自己持有:
a)alloc
使用类的alloc方法就能生成一个对象,在这里,指向生成并持有的对象的指针被赋值给变量obj。
b ) new
在Objective-C中,[[NSObject alloc] init]和[NSObject new]是完全一致的。
c)copy
d )mutableCopy
copy和mutableCopy都是生成并持有对象的一份副本,虽然持有的是副本,但是,副本是自己生成的。
另外,除以上的几个,以下名称开头的方法也意味着自己生成并持有。
(1)allocMyObject
(2)newThatObject
(3)copyThis
(4)mutableCopyYourObject
但是对于一下名称,即使用alloc/new/copy/mutableCopy名称开头,并不属于同一类方法。
(1)allocate
(2)newer
(3)copying
(4)mutableCopy
问题二:
什么情况下自己非自己生成的对象,自己也能持有?
用alloc/new/copy/mutableCopy等开头以外的方法取得的对象,因为不是自己生成的对象,所以并不持有这个对象。比如一些类方法。
取到一个非自己生成的对象,但是自己可以持有对象的实现如下,向对象发送autorelease消息,告诉系统这个对象需要被引用,延缓释放:
其实,由类方法创建的对象都会放入到NSAutoreleasePool中。后面会继续讲。
问题三:
如何释放自己持有的对象?
向自己持有的对象发送release消息。
这里有个需要弄明白的:
1)释放 !== 销毁
一个对象可以有多个持有者,且持有者只能向自己持有的对象发送release消息,由引用计数器原则,当一个对象的retainCount == 0,这个对象才会被销毁。多个持有者时,每次release都会使对象的retainCount - 1。
实例如下代码:
所以,释放跟销毁不是一个概念,它们并不等价。
2)赋值 !== 持有
下面这句代码,NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有改对象。所以,赋值并不等价于就是持有。
问题四:
对于“非自己持有的对象,自己无法释放“这句话的解释。
其实就是被释放或被销毁的对象不能重复被引用。
第二次释放的时候程序会崩溃。
二、alloc/retain/release/dealloc
关于alloc/retain/release/dealloc的实现可参看GNUstep,亦可以查阅 《Objective - C高级编程》这本书上的讲解。这里就不赘述了。
三、引用计数的实现
这一块儿理解的不是很好,大致的是这么回事儿:alloc类方法首先调用allocWithZone:类方法,然后调用create_Instance函数,然后通过调用calloc方法来分配内存。Apple内部使用一个引用计数表来管理所有对象的引用计数,同时,引用计数表还记录着每个对象的内存块地址。每次调用release方法都会去技术表中查询对象的引用计数的值。
具体总结如下:
(1)在Objective-C这一对象中存在引用计数器这一整数值。
(2)调用alloc 或者retain方法后,引用计数值会加1.
(3)调用release后,引用计数值减1.
(4)引用计数值为0时,调用dealloc方法废弃对象。
在实际开发中,内存泄漏还是比较常见的,比如使用block时候的不注意,导致的循环引用;比如在使用NSTimer实例的时候,timer的action事件执行者也就是Target会强引用Self,如果timer没有正确的销毁,就会造成内存泄漏等等;这个时候我们就需要来手动管理内存了。因此,很有必要知道Objective -C的内存管理机制。
二、Objective-C内存管理是机制,就是引用计数。
正确客观的思考方式:
1)自己生成的对象,自己持有。
2)非自己生成的对象,自己也能持有。
3)自己持有的对象,一旦不再需要,就需要立即释放。
4)非自己持有的对象,自己无法释放。
这里的“自己”,知道是当前的对象的使用环境,也可以理解为程序猿自己啦。
问题一:
什么情况下自己生成的对象,自己持有?
使用以下名称开头的方法名,意味着自己生成的对象只有自己持有:
a)alloc
// 自己生成并持有 id obj = [[NSObject alloc] init];
使用类的alloc方法就能生成一个对象,在这里,指向生成并持有的对象的指针被赋值给变量obj。
b ) new
// 自己生成并持有对象 id obj2 = [NSObject new];
在Objective-C中,[[NSObject alloc] init]和[NSObject new]是完全一致的。
c)copy
d )mutableCopy
copy和mutableCopy都是生成并持有对象的一份副本,虽然持有的是副本,但是,副本是自己生成的。
另外,除以上的几个,以下名称开头的方法也意味着自己生成并持有。
(1)allocMyObject
(2)newThatObject
(3)copyThis
(4)mutableCopyYourObject
但是对于一下名称,即使用alloc/new/copy/mutableCopy名称开头,并不属于同一类方法。
(1)allocate
(2)newer
(3)copying
(4)mutableCopy
问题二:
什么情况下自己非自己生成的对象,自己也能持有?
用alloc/new/copy/mutableCopy等开头以外的方法取得的对象,因为不是自己生成的对象,所以并不持有这个对象。比如一些类方法。
// 取得非自己生成的对象,自己并不持有 id array = [NSMutableArray array]; // 如果要持有对象,那么就向对象发送retain消息 [array retain];
取到一个非自己生成的对象,但是自己可以持有对象的实现如下,向对象发送autorelease消息,告诉系统这个对象需要被引用,延缓释放:
- (id) object{ id obj = [[NSObject alloc] init]; [obj autorelease]; //obj 放入到NSAutoreleasePool中,延迟释放。 //取得的对象存在,但是自己不持有对象 return obj; }
其实,由类方法创建的对象都会放入到NSAutoreleasePool中。后面会继续讲。
问题三:
如何释放自己持有的对象?
向自己持有的对象发送release消息。
// 自己生成并持有
NSArray *arr1 = @[@"11",@"22"];
// 释放自己生成且持有的对象
[arr1 release];
// 取得非自己生成的对象,自己并不持有 id array = [NSMutableArray array]; // 如果要持有对象,那么就向对象发送retain消息 [array retain];
// 释放自己持有的对象
[array release];
这里有个需要弄明白的:
1)释放 !== 销毁
一个对象可以有多个持有者,且持有者只能向自己持有的对象发送release消息,由引用计数器原则,当一个对象的retainCount == 0,这个对象才会被销毁。多个持有者时,每次release都会使对象的retainCount - 1。
实例如下代码:
// 创建并持有一个对象。 id obj = [NSObject new]; NSLog(@" create new obj === %lu ===",[obj retainCount]);//retainCount=1 NSMutableArray *mutableArray = [NSMutableArray array]; // 数组对象持有 对象 obj [mutableArray addObject:obj]; NSLog(@" mutableArray have obj === %lu ===",[obj retainCount]); //retainCount=2 id dict = [NSMutableDictionary dictionary]; // 字典对象持有 NSObject对象 obj [dict setObject:obj forKey:@"oc"]; NSLog(@" dict have obj === %lu ===",[obj retainCount]);//retainCount=3 // 字典对象释放 [dict release]; NSLog(@" dic release === %lu ===",[obj retainCount]);//retainCount=2 // 数组对象释放 [mutableArray release]; NSLog(@" mutableArray release === %lu ===",[obj retainCount]);//retainCount=1 // obj对象释放 [obj release]; NSLog(@" obj release === %lu ===",[obj retainCount]);//retainCount=1
所以,释放跟销毁不是一个概念,它们并不等价。
2)赋值 !== 持有
下面这句代码,NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有改对象。所以,赋值并不等价于就是持有。
id array = [NSMutableArray array];
问题四:
对于“非自己持有的对象,自己无法释放“这句话的解释。
其实就是被释放或被销毁的对象不能重复被引用。
// 创建并持有一个对象。 id obj = [NSObject new]; NSLog(@" create new obj === %lu ===",[obj retainCount]); // obj对象释放 [obj release]; NSLog(@" obj release === %lu ===",[obj retainCount]); // obj对象释放 [obj release]; NSLog(@" obj release === %lu ===",[obj retainCount]);
第二次释放的时候程序会崩溃。
二、alloc/retain/release/dealloc
关于alloc/retain/release/dealloc的实现可参看GNUstep,亦可以查阅 《Objective - C高级编程》这本书上的讲解。这里就不赘述了。
三、引用计数的实现
这一块儿理解的不是很好,大致的是这么回事儿:alloc类方法首先调用allocWithZone:类方法,然后调用create_Instance函数,然后通过调用calloc方法来分配内存。Apple内部使用一个引用计数表来管理所有对象的引用计数,同时,引用计数表还记录着每个对象的内存块地址。每次调用release方法都会去技术表中查询对象的引用计数的值。
具体总结如下:
(1)在Objective-C这一对象中存在引用计数器这一整数值。
(2)调用alloc 或者retain方法后,引用计数值会加1.
(3)调用release后,引用计数值减1.
(4)引用计数值为0时,调用dealloc方法废弃对象。
相关文章推荐
- Lua的内存管理浅析
- 跟我学习JScript的Bug与内存管理
- 跟我学习javascript的垃圾回收机制与内存管理
- 深入探讨PHP中的内存管理问题
- Objective-C的内省(Introspection)用法小结
- linux 内存管理机制详细解析
- 解析PHP中的内存管理,PHP动态分配和释放内存
- javascript内存管理详细解析
- JavaScript内存管理介绍
- Cocos2d-x的内存管理总结
- 模拟实现C语言中的内存管理
- 深入解析PHP的引用计数机制
- javascript错误的认识不用关心内存管理
- IOS中内存管理那些事
- Objective-C中常用的结构体NSRange,NSPoint,NSSize(CGSize),NSRect实例分析
- Objective-C中NSNumber与NSDictionary的用法简介
- Objective-C中NSLog输出格式大全
- Python深入学习之内存管理
- 全面解析Objective-C中的block代码块的使用
- Swift调用Objective-C编写的API实例