ARC
在LLVM编译器中设置ARC
为有效状态,就无需再次输入retain
或者release
代码
照明对比引用计数 | 对照明设备所做的动作 | 对oc对象所做的动作 | 引用技数 | oc方法 |
---|---|---|---|---|
第一个进入办公室的人 | 开灯 | 生成并持有对象 | 0 --- >1 | alloc,new,copy,mutableCopy等方法 |
之后每当有人进入办公室 | 需要照明 | 持有对象 | 1 --- >2 | retain方法 |
每当有人下班离开办公室 | 不需要照明 | 释放对象 | 2 --- > 1 | release方法 |
最后一个人下班离开办公室 | 关灯 | 废弃对象 | 1 ----> 0 | dealloc方法 |
alloc
,retain
,release
,dealloc
的实现总结,
1, 在oc的对象中存有应用计数这一整数值
2, 调用alloc
或retain
方法后,引用计数值加1
3, 调用release
后,引用计数值减1
4, 引用计数值为0时,调用dealloc
方法废弃对象
内存管理的思考方式
-
自己生成的对象,自己所持有
- 自己生成并持有对象的源代码
/*
使用 NSObject类的alloc类方法就能自己生成并持有对象,指向生成并持有对象的指针被赋给变量obj,[NSObject new]与[[NSObject alloc]init]是完全一致的
*/
id obj = [[NSObject alloc] init];
id obj = [NSObject new];
- 使用以下方法也可以生产并持有对象
alloc | new | copy | mutableCopy |
---|
copy
方法利用基于NSCopying
方法约定,由各类实现的copyWithZone:
方法生成并持有对象的副本.
mutableCopy
方法利用基于NSMutableCopying
方法约定,由各类实现的mutableCopyWithZone:
方法生成并持有对象的副本.
两者的区别在于,copy
方法生成不可变更的对象,而mutableCopy
方法生产可变更的对象
-
使用以下名称开头的方法名,也意味着自己生成并持有对象
allocMyObject
newThatObject
copyThis
mutableCopyYourObject
-
非自己生成的对象,自己也能持有
通过
retain
方法,非自己生成的对象跟用alloc
,new
,copy
,mutableCopy
方法生成并持有的对象一样,成为了自己所持有的.源码展示
/*
NSMutableArray类对象被赋给变量obj,但变量obj自己并不持有该对象,但retain方法可以持有对象
*/
id obj = [NSMutableArray array];
[obj retain];
```
- 不再需要自己持有的对象时要释放
- 自己持有的对象,一旦不再需要,持有者有义务释放该对象.释放使用``release``方法
- 源码展示
```objc
// 用alloc方法由自己生成并持有的对象就通过release方法释放了
/* 自己生成并持有对象*/
id obj = [[NSObject alloc] init];
/* 释放对象,*/
[obj release];
```
- 取得非自己生产并持有的对象
- 源码展示
``` objc
/*取得的对象存在, 自己不持有对象*/
id obj = [NSMutableArray array];
/* 自己持有对象*/
[obj retain];
/* 释放对象,对象不可再被访问*/
[obj release];
```
- 用某个方法生成对象,并将其返还给该方法的调用方
> 原封不动的返回用``alloc``方法生成并持有的对象,就能让调用方也持有该对象, ``allocObject``是符合驼峰命名规范的,即将第一个词后每个词的首字母大写来拼写复合词的记法. 外部调用,``id obj1 = [obj0 allocObject];``,即自己持有对象
- 源码展示
``` objc
- (id)allocObject{
/* 自己生成并持有对象*/
id obj = [[NSObject alloc] init];
/* 自己持有对象*/
return obj;
}
```
- 调用``[NSMutableArray array]`` 方法使取得的对象存在,但自己不持有对象
> 使用autorelease方法,可以使取得的对象存在,但自己不持有对象,``autorelease``提供这样的功能,使对象在超出置顶的生存范围时能够自动并正确地释放(调用``release``方法)
- 源码展示
```objc
- (id)object
{
/* 自己生成并持有对象*/
id obj = [[NSObject alloc] init];
/* 自己持有对象*/
[obj autorelease];
/* 取得的对象存在,但自己不持有对象 */
return obj;
}
- 外部调用
// 取得的对象存在,但自己不持有对象
id obj1 = [obj0 object];
// 通过retain方法将调用autorelease方法取得的对象变为自己持有
[obj1 retain];
```
- 非自己持有的对象无法释放
自己生成并持有对象后,在释放完不再需要的对象之后再次释放
/* 自己生成并持有对象*/
id obj = [[NSObject alloc]init];
/* 对象已释放*/
[obj release];
/* 释放之后再次释放已非自己持有的对象,应用程序崩溃!
崩溃情况: 再度废弃已经废弃了的对象时崩溃,访问已经废弃的对象时崩溃
*/
[obj release];
```
> 取得的对象存在,但自己不持有对象时释放
```objc
/*取得的对象存在,但自己不持有对象*/
id obj1 == [obj0 object];
/* 释放了非自己持有的对象,肯定会导致应用程序崩溃*/
[obj1 release];
```
# ``alloc``,``retain``,``release``,``dealloc``实现
> CGUstep是Cocoa框架的互换框架,从使用者的角度来看,两者的行为和实现凡是是一样的
- ``GNUstep``源代码中``NSObject``类的类方法实现
- ``+ (id)alloc``通过``+(id)allocWithZone:(NSZone * )z``调用``NSAllocateObject``函数分配了对象,``NSAllocateObject``函数又通过调用``NSZoneMalloc``函数来分配存放对象所需的内存空间,之后将该内存空间置为0,最后返回作为对象而使用的指针
- ``alloc``类方法用``struct obj_layout``中的``retained``整数来保存引用计数,并将其写入对象内存头部,该对象内存块全部置为0后返回
- 对象的引用计数可通过``retainCount``实例方法取得,内部调用``NSExtraRefCount``函数,执行``alloc``后对象的``retainCount``是``1``.
- 由对象寻找到对象内存头部,从而访问其中的``retained``变量,因为分配时全部置为0,所以``retained``为0,由``retainCount``方法内部的``NSExtraRefCount(self)+1``得出,``retainCount`为1.即``retain``方法使``retained``变量加1,而``release``方法使``retained``变量减1.
- 苹果的实现
- ``alloc``类方法首先调用``allocWithZone:``类方法,然后调用``class_createInstance``函数,最后通过`calloc``来分配内存块
- ``retainCount``,``retain``,``release``各方法都调用了``_CFDoExternRefOperation``函数,苹果的实现大概就是采用散列表(引用计数表)来管理引用计数
- GNUstep和苹果实现进行对比
- ``GNUstep``将引用计数保存在对象占用内存块头部的变量中,而苹果则是保存在引用技术表的记录中
- 内存块管理的好处:
> 1.少量的代码即可完成
2.能过统一管理引用计数用内存块与对象用内存块
- 通过引用技术表管理引用计数的好处:
> 1. 对象用内存块的分配无需考虑内存块头部
2.引用计数表各记录中存有内存块,可从各个记录追溯到各对象的内存块.
# autorelease使用
> ``autorelease``自动释放,类似于C语音中局部变量的特性(程序执行时,若某自动变量超出其作用域,该自动变量将被自动废弃),``autorelease``会在对象实例超出其作用域时,调用对象实例的``release``方法.
- ``autorelease``的具体使用方法如下:
> 1.生成并持有``NSAutoreleasePool``对象
2.调用已分配对象的``autorelease``实例方法
3.废弃``NSAutoreleasePool``对象,将调用``release``方法
# ``autorelease``实现
- ``GNUstep``中实现
> ``autorelease``实例方法的本质就是调用``NSAutoreleasePool``对象的``addObject``类方法.
- 苹果的实现
> 1.``static inline void *push()`` 相当于生成或持有``NSAutoreleasePool``类对象
2.``static inline void *pop(void *token)``内部为``releaseAll()``方法, 相当于废弃``NSAutoreleasePool``类对象
3.``static inline id autorelease(id obj)``内部调用``add(obj)``方法(将对象追加到内部数组中),相当于``NSAutoreleasePool``类的``addobject``类方法
- 相关问题
> 如果NSAutoreleasePool对象调用 autorelease会如何?
```objc
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pool release];
会发生异常,在使用oc的Foundation框架时,无论使用哪一个对象的autorelease实例方法,实际上都是调用NSObject类的autorelease实例方法.但是对于NSAutoreleasePool类,autorelease实例方法已被该类重载,因此运行时就会出错