在《Objective-C高级编程:iOS与OS X多线程和内存管理》一书中,对于ARC的部分有详尽的讲解。我将这一部分整理成思维导图,算是一个归档吧。

内存管理之ARC部分

如下是一些需要注意的点:

0.id

  • id是OC中对象标识的数据类型,可以用来指定任何类。id是所有对象的终极超类。
  • <objc/objc.h>id的定义:
1
2
3
4
5
6
7
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
  • 虽然看起来是指向结构体的指针,但对于OC的编译器而言,id使得原本的静态类型的语言变得动态起来。这就意味着,它指向的对象的类型是不被编译器检查的。
  • ARC有效时,id类型和对象类型同C语言其他类型不同,其类型上必须附加所有权修饰符,包括:__strong__weak__unsafe_unretain__autoreleasing修饰符。
  • __strong__weak__autoreleasing修饰符一起,可以保证将附有这些修饰符的自动变量初始化为nil

1.引用计数规则

  • 谁使用谁释放,谁创建谁释放。(谁污染谁治理 =。=)

2.是在预编译/编译/链接/运行 哪个阶段进行?

  • 所有的retain、release、strong属性的会在编译时处理。
  • weak属性会在运行时处理。

3.__strong__weak__unsafe_unretain

  • __strong:是id类型和对象类型默认的所有权修饰符。附有__strong修饰符的变量,在超出其变量作用域时,即在该变量被废弃时,会释放其被赋予的对象。表示逻辑上强持有一个对象。规则是:指针被赋值时同时进行retain处理;指针销毁时对指针的内容进行release。
  • __weak:指针被赋值时,不会对所赋的对象进行retain,意味着引用计数会随时为零,一旦为零,就会被置为nil,也就是说,要再引用计数为零之前置为nil
  • __unsafe_unretain:不安全的不持有。规则是:声明一个指针,对赋值的对象不进行retain,如果对象为零了,那么指针会继续指向这个对象。
  • __weak会有性能损耗,所以在处理极大对象的时候会使用__unsafe_unretain属性。

4.strongweakassign

  • strong:和__strong是一样的,在dealloc的时候release。
  • weak:和__weak是一样的,一旦引用计数变为零,会自动置为nil
  • assign:能够声明很多标量(float,int等),用它来声明变量时,和__unsafe_unretain一样,声明一个指针的时候会对它进行持有,当为零的时候也不会自动释放。

5.weak详解

  • 工作在运行时:编译时是按照逻辑编译单元(一个.m文件)进行编译的,在一个编译单元中无法知道其他编译单元的情况,无法判断是否为零、是否需要置为nil
  • 如何置为nil:底层有一个哈希表,哈希表中存储所有weak指向的对象。用weak指针指向的值(对象的地址,是一个栈/堆上的变量)作为key,所有指向它的地址作为value。每当对象dealloc时,就会检查这个表,同时把表中所有指向这个对象的指针都置为nil。

6.归零弱引用(Zeroing weak reference)

即让对象自己去清空弱引用的对象,这种特殊的弱引用被称为归零弱引用。在它指向的对象释放后,这些弱引用会被设置为零(即nil)。

有两种方式可以声明归零弱引用:

  • 声明变量时使用__weak关键字或对属性使用weak特性。
  • 在不支持弱引用的旧系统上,可以使用苹果公司提供的__unsafe_unretained关键字和unsafe_unretained特性。

7.循环引用

  • a.propertyB = b; b.propertyA = a; b的引用计数+1,a的引用计数+1。引用计数一直无法为零,造成循环引用。
  • 如何解?
    • 原因在于,b和a都会引用计数至少为1。
    • 暴力解法:b或者a多调用一下release。然后就挂了。ㄟ( ▔. ▔ )ㄏ
    • 简单解法:任何一端置为nil。

Ref