Objective-C 内存管理之思考方式
引用计数概念解释
Objective-C中的内存管理,也就是引用计数。可以用开关房间的灯为例来说明引用计数的机制。
假如办公室里的照明设备只有一个。上班进入办公室的人需要照明,所以要把灯打开。而对于下班离开办公室的人来说,已经不需要照明了,所以要把灯关掉。
解决这一问题的办法是使办公室在还有至少1人的情况下保持开灯状态,而在无人时保持关灯状态。
- 最早进入办公室的人开灯。
- 之后进入办公室的人,需要照明。
- 下班离开办公室的人,不需要照明。
- 最后离开办公室的人关灯(此时已无人需要照明)。
为判断是否还有人在办公室里,这里导入计数功能来计算“需要照明的人数”。
- 第一个进入办公室,需要照明的人数+1。计数值从0变成了1,因此要开灯。
- 之后每当有人进入办公室,需要照明的人数就+1。
- 每当有人下班离开办公室,需要照明的人数就-1.
- 最后一个人下班离开办公室是,需要照明的人数就-1,当计数值从1变成了0,因此要关灯。
这样就能在不需要照明的时候保持关灯状态。办公室中仅有的照明设别得到了很好的管理。
在Objective-C中,“对象”相当于办公室的照明设备。在显示世界中办公室的照明设备只有一个,但在OCzhong ,可以同时处理好多对象。
对照明设备所做的动作 | 对OC对象所做的动作 | OC对应的方法 |
---|---|---|
开灯 | 生成对象 | alloc/new/copy/mutableCopy等方法 |
需要照明 | 持有对象 | retain方法 |
不需要照明 | 释放对象 | release方法 |
关灯 | 废弃对象 | dealloc方法 |
内存管理的思考方式
正确的思考方式是:
- 自己生成对象,自己所持有。
- 非自己生成的对象,自己也能持有。
- 不再需要自己持有的对象时释放。
- 非自己持有的对象无法释放。
Cocoa框架中Foundation框架类库的NSObject类负担内存管理的指责。OC内存中的alloc/retain/release/dealloc方法分别只带NSObject类的alloc类方法,retain实例方法,release实例方法和dealloc实例方法。
自己生成的对象,自己持有
使用以下名称开头的方法名意味这自己生成的对象只有自己持有:
- alloc
- new
- copy
- mutableCopy
注:自己指 对象的只用环境
例如:
/* * 自己生成并持有对象 */ id obj = [[NSObject alloc] init]; // id obj = [NSObject new]; /* * 自己持有对象 */
使用NSObject类的alloc类方法就能自己生成并持有对象。只想生成并持有对象的指针被赋给变量obj。另外new类方法也可以。
copy方法利用基于NSCopying方法约定,由各类实现的copyWithZone:生成并持有对象的副本。与copy方法类似,mutableCopy方法利用给予NSMutableCopying方法约定,由各类实现的mutableCopyWithZone:方法生成并持有对象的副本。两者的区别在于,copy方法生成不可变更的对象,而mutableCopy方法生成可变更的变量。
非自己生成的对象,自己也能持有
用上述项目之外的方法缺的对象,因为非自己生成并持有,所有自己不是该对象的的持有者。例如NSArray类的array类方法。
/* * 取得非自己生成并持有的对象 */ id obj = [NSMutableArray array] ; /* * 对象已存在,但自己不持有对象 */ [obj retain]; /* * 自己持有对象 */
代码中,NSMutableArray对象被赋给变量obj,但obj自己并不持有该对象。使用retain方法可以持有对象。
不再需要自己持有的对象时释放
自己持有的对象,一旦不再需要时,持有这有义务释放该对象。释放使用release方法。
/* * 自己生成并持有的对象 */ id obj = [NSObject new] ; /* * 自己持有对象 */ [obj release]; /* * 自己释放对象 * 只想对象的指针仍然被保留在变量obj中,貌似能够访问,但对象一经释放绝对不可访问 */
用alloc/new方法由自己生成并持有的对象就通过release方法释放了。自己生成而非自己所持有的对象,若用retain方法变为自己持用,也同样用release方法释放。
如果要用某个自定义方法生成对象,并将其返回给该方法的调用方,那么代码如下:
- (id) allocObject { /* * 自己生成并持有对象 */ id obj = [[NSObject alloc] init]; /* * 自己持有对象 */ return obj; }
如上所示,原封不动第返回用alloc方法生成并持有的对象,就能让调用方法也持有该对象。请注意该方法的命名规则,是以alloc开头命名的,那么自定义方法中,以alloc/new/copy/mutableCopy命名的方法,也意味自己生成并持有对象。
/* * 取得非自己生成并持有对象 */ id obj1 = [obj0 allocObject]; /* * 自己持有对象 */
类似于[NSMutableArray array]的自定义方法如下(命名不能以alloc/new/copy/mutableCopy开头)
- (id) Object { id obj = [[NSObject alloc] init]; /* * 自己持有对象 */ [obj autorelease]; /* * 取得对象的存在,但自己不持有对象 */ return obj; }
id obj1 = [obj0 Object]; /* * 取得的对象存在,但不自己持有 */ [obj1 retain]; /* * 自己持有对象 */
上述方法中,使用了autorelease方法。用该方法,可以使取得的对象存在,但不自己持有。autorelease方法提供使对象在超出的生存范围时能够自动并正确的释放。
- release为调用之后立即释放
- autorelease为调用之后注册到autoreleasepool中,pool结束时自动调用release,aoturelease不是立即释放,而存在一定时长
无法释放非自己持有的对象
对于用alloc/new/copy/mutableCopy方法生成并持有的对象,或是用retain方法持有的对象,由于持有者是自己,所以在不需要该对象的时候需要将其释放。而由此意外所得到的对象绝对不能释放。如果强行释放,则会造成crash。
/* * 自己生成并持有对象 */ id obj = [NSObject new]; /* * 自己持有对象 */ [obj release]; /* * 对象已释放 */ [obj release]; /* *对象释放后再次释放已非自己持有的对象,导致crash */
id obj1 = [obj0 Object]; /* * 取得的对象存在,但不自己持有 */ [obj1 release]; /* * 释放了非自己持有的对象,导致crash */
以上,就是“引用计数式内存管理”的思考方式。