阅读 107

《Objective-C 高级编程》学习笔记1-内存管理

狮子头镇楼

《Objective-C 高级编程》学习笔记2-Blocks

《Objective-C 高级编程》学习笔记3-GCD

1、什么是自动引用计数

  • 内存管理中对引用采取自动计数的技术
  • 在编译器中设置 ARC 为有效状态,就无需再次键入 retain 或者是 release 代码

2、内存管理 / 引用计数

2.1   内存管理的思考方式

  • 自己生成的对象,自己所持有
  • 非自己生成的对象,自己也能持有
  • 不再需要自己持有的对象时释放
  • 非自己持有的对象无法释放

2.2   alloc/retaion/release/dealloc 实现

GNUstep 是 Cocoa 框架的互换框架

2.2.1、GNUstep 中 alloc 类方法实现的简化版:

struct obj_layout {
    NSUInteger retained;
};

+ (id)alloc {
    int size = sizeof(struct obj_layout) + 对象大小;
    struct obj_layout *p = (struct obj_layout *)calloc(1, size);
    return (id)(p + 1);
}
复制代码

2.2.2、alloc 返回对象的内存图:

2.2.3、GNUstep 内存管理总结:

  • 在 OC 对象中存有引用计数这一整数值
  • 调用 alloc 或是 retaion 方法后,引用计数值加1
  • 调用 release 后,引用计数值减1
  • 引用计数值为0时,调用 dealloc 方法废弃对象

2.3   内存管理-苹果的实现

// __CFDoExternRefOperation 函数按 retainCount/retain/release 操作进行分发调用不同函数
// NSObject 类的相关方法实现如下:

- (NSUInteger)retainCount {
    return (NSUInteger)__CFDoExternRefOperation(OPERATION_retainCount, self);
}

- (id)retain {
    return (id)__CFDoExternRefOperation(OPERATION_retain, self);
}

- (void)release {
    __CFDoExternRefOperation(OPERATION_release, self);
}
复制代码

GNUstep 将引用计数保存在对象占用内存块头部的变量中,

苹果则是保存在引用计数表的记录中:

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

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

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

  • 对象用内存块的分配无需考虑内存块头部
  • 引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块

3、ARC 规则

3.1   所有权修饰符

OC 编程中为了处理对象,可将变量类型定义为 id 类型或各种对象类型:

  • 对象类型就是指向 NSObject 这样的OC类的指针,例如“NSObject *”
  • id 类型用于隐藏对象类型的类名部分,相当于C语言中常用的“void *”

ARC有效时,id类型和对象类型必须附加所有权修饰符:

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

3.1.1、__strong修饰符:默认的所有权修饰符

__strong修饰符表示对对象的“强引用”。持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。

**3.1.2、__weak修饰符:避免引用循环,**弱引用不能持有对象实例

循环引用容易造成内存泄漏,内存泄漏就是应当废弃的对象在超出其生存周期后继续存在。

__weak修饰符,在持有某对象的弱引用时,若该对象被废弃,则此弱引用将自动失效且处于nil被赋值的状态。

3.1.3、__unsafe_unretained修饰符:在iOS以下中用来代替__weak

附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象

赋值给附有__unsafe_unretained修饰符变量的对象在通过该变量使用时,如果没有确保其确实存在,则应用程序会崩溃。

3.1.4、__autoreleasing修饰符

ARC有效时,指定“@autoreleasepool块”替代“NSAutoreleasePool”类,用附有 __autoreleasing 修饰符的变量替代 autorelease 方法。

不使用 __autoreleasing 修饰符也能使对象注册到 autoreleasepool。由于return使强引用持有的对象超出作用域会被释放,但该对象作为函数的返回值,编译器会自动将其注册到 autoreleasepool。

在访问附有 __weak 修饰符的变量时,实际上必定要访问注册到 autoreleasepool 的对象。

3.2   规则

在 ARC 有效的情况下编译源代码,必须遵守一定的规则:

  • 不能使用 retain/release/retainCount/autorelease
  • 不能使用 NSAllocateObject/NSDeallocateObject
  • 须遵守内存管理的方法命名规则
  • 不要显示调用 dealloc
  • 使用 @autoreleasepool 块替代 NSAutoreleasePool
  • 不能使用区域(NSZone)
  • 对象型变量不能作为C语言结构体的成员
  • 显示转换“id” 和 “void *”

3.2.3、ARC 有效时须遵守内存管理的方法命名规则

在 ARC 无效时,用于对象 生成/持有 的方法必须遵守命名规则:

  • alloc
  • new
  • copy
  • mutableCopy

在 ARC 有效时,以上规则没有改变。只是要追加一条命名规则:

  • init,该方法会初始化 alloc 方法返回的对象,然后原封不动地返还给调用方

3.3   属性

当 ARC 有效时,Objective-C 类的属性也会发生变化: