美文网首页
《OC高级编程》之自动引用计数(一)

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

作者: 毛线sama | 来源:发表于2019-03-07 22:56 被阅读0次

    内存管理/引用计数

     

    思考方式

    • 自己生成的对象,自己所持有
    • 非自己生成的对象,自己也能持有
    • 不再需要自己持有的对象时释放
    • 非自己持有的对象无法释放
    对象操作 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高级编程》之自动引用计数(一)

          本文链接:https://www.haomeiwen.com/subject/wgukpqtx.html