《OC高级编程》之自动引用计数(一)

内存管理/引用计数

 

思考方式

  • 自己生成的对象,自己所持有
  • 非自己生成的对象,自己也能持有
  • 不再需要自己持有的对象时释放
  • 非自己持有的对象无法释放
对象操作 OC方法
生成并持有对象 alloc/new/copy/mutableCopy
持有对象 retain
释放对象 release
废弃对象 dealloc

非自己生成的对象,想持有得通过 retain 方法

使用以下名称开头的方法也意味着自己生成并持有对象: allocMyObject/newThatObject/copyThis/mutableCopyObject

//自己生成并持有的对象
let obj = [[NSObject alloc] init];
let obj1 = [NSObject new];
//自己持有对象

[obj release];
[obj1 release];
//释放对象,指针仍保留,貌似能访问,释放的对象绝对不可访问

//非自己生成的对象
let obj2 = [NSMutableArray array];
//对象存在,但不持有
[obj2 retain];
//持有对象
[obj2 release];
//释放

​ 生成对象方法的源代码实现:

//自己生成并持有
- (id)allocObject
{
    id obj = [[NSObject alloc] init];
    return obj
}
//生成但不持有
- (id)object
{
    id obj = [[NSObject alloc] init];
    //autorelease可以使取得的对象存在,但不持有
    [obj autorelease];
    return obj
}

autorelease 使对象在超出指定生存范围时能自动并正确释放(调用 release ) release 方法会立即释放对象,而 autorelease 则是把对象注册到 autoreleasepool 中,pool 结束时自动调用 release

​ 释放非自己持有的对象程序会奔溃

let obj = [[NSObject alloc] init];
[obj release];
[obj release];
//奔溃
//情况:再次废弃已废弃的对象时崩溃;访问已废弃对象时奔溃
let obj1 = [NSObject object];
[obj1 release];
//奔溃

 

alloc/retain/release/dealloc实现

    GNUStep 中 alloc 是通过 allocWithZone: 类方法调用 NSAllocateObject 函数分配对象,NSAllocateObject 函数通过调用 NSZOneMallioc 函数分配存放对象所需的内存空间,之后再将内存空间置 0,最后返回作为对象而使用的指针

NSZone 是为防止内存碎片化而引入的结构,对内存分配的区域本身进行多重化管理,根据对象的目的,大小分配内存,从而提高管理效率(但是现在的运行系统内存管理本身已极具效率,没啥卵用

    去掉 NSZone 后,alloc 类方法用 struct obj_layout 中的 retained 整数来保存引用计数,并将其写入对象内存头部,将对象内存块全部置 0 后返回

//alloc简化版
struct obj_layout{
    NSUInteger retained;
    //[obj retainCount]可获取该值
}
+ (id)alloc
{
    int size = sizeof(struct obj_layout)+对象大小;
    struct obj_layout *p = (struct obj_layout *)calloc(1,size);
    return (id)(p+1);
}

calloc 方法在内存的动态存储区中分配 n 个长度为 size 的连续空间,函数返回一个指向分配起始地址的指针,分配完内存后,自动初始化该内存空间为零( malloc 则不初始化,里边数据是随机的垃圾数据)

    retain 方法使 retained 变量加一,release 则减一,当 retained 等于0时 dealloc,dealloc 时进行 free()

 

苹果的实现

    苹果是将引用计数保存在引用计数表(散列表)的记录里(表键值为内存块地址的散列值)

  通过内存块头部管理引用计数的好处:

  • 少量代码即可完成
  • 能统一管理引用计数用内存块与对象用内存块

  通过引用计数表管理的好处:

  • 对象用内存块无需考虑内存块头部
  • 引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块(即使出现故障导致对象内存块损坏,也可以通过计数表确认位置)

 

autorelease

    有点类似于 C 语言中局部变量的特性,超过作用域就自动废弃,即对象实例的release 方法被调用,与 C 语言不同的是,可以设定变量的作用域

  1. 生成 NSAutoreleasePool 对象
  2. 调用已分配对象的 autorelease 实例方法
  3. 废弃 NSAutoreleasePool 对象( object 自动调用 release )

​ NSAutoreleasePool 生存周期相当于 C 语言变量的作用域

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain] //等同于[obj release]

    相当于程序主循环的 NSRunLoop 会自动生成,持有并废弃NSAutoreleasePool 对象,不需要非得使用 NSAutoreleasePool 对象,但在大量生成 autorelease 对象时,可能会内存不足,有必要手动在适当地方生成,持有并废弃 NSAutoreleasePool

    有很多类方法用于返回 autorelease 对象:[NSMutableArray arrayWithCapacity:1]

 

autorelease 实现

    GNUStep 中 autorelease 实例方法实质是调用 NSAutoreleasePool 对象的addObject 类方法

GNUStep 实际是用称为“ IMP Caching ”的方法实现的,在进行方法调用时,在框架初始化时对类名/方法名,以及取得方法运行时的函数指针的结果值进行缓存,速度是直接调用 [NSAutoreleasePool addObject: self] 方法的两倍

    addObject 类方法调用正在使用的 NSAutoreleasePool 对象的 addObject 实例方法,使用的是连接列表,调用 autorelease 方法的对象被追加到NSAutoreleasePool 对象的数组中。drain 实例方法则先 realse array 里的每一个对象,再 release array

 

苹果的实现

    和 GUNStep 完全相同,可通过 [NSAutoreleasePool showPools] 确定已被autorelease 的对象的状况

autorelease NSAutoreleasePool 对象会发生异常

你可能感兴趣的:(《OC高级编程》之自动引用计数(一))