1、引用计数(Reference Counting)

引用计数是Objective-C中的内存管理方式。
对象操作与Objective-C中方法的对应关系:

对象操作 Objective-C方法
生成并持有对象 alloc/new/copy/mutableCopy
持有对象 retain
释放对象 release
废弃对象 dealloc

使用以下名称开头的方法名,将自己生成并持有对象

  • allocXXX
  • newXXX
  • copyXXX
  • mutableCopyXXX

    copy方法基于NSCopying协议方法约定,由各类实现的copyWithZone:方法生成并持有对象的副本 mutableCopy与copy类似,基于NSMutableCopying协议 两者的区别: 1.copy生成不可变更的对象 2.mutableCopy生成可变更的对象

Autorelease

autorelease顾名思义就是自动释放。调用autorelease会在变量超过自身作用域的时候自动调用release进行释放。autorelease作用域就需要提及NSAutoreleasePool对象池。
使用过程

1.生成并持有NSAutoreleasePool对象 2.调用已经分配对象的autorelease方法 3.废弃NSAutoreleasePool对象

  1. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //创建pool
  2. id obj = [[NSObject alloc] init];
  3. [obj autorelease]; //调用autorelease
  4. [pool drain]; // 废弃pool

Autoreleasepool补充

在没有手加Autorelease Pool的情况下,Autorelease对象是在当前的runloop迭代结束时释放的,而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop。

Autoreleasepool的原理

ARC下,我们使用@autoreleasepool{}来使用一个AutoreleasePool,随后编译器将其改写成下面的样子:
void *context = objc_autoreleasePoolPush();``// {}中的代码objc_autoreleasePoolPop(context);
而这两个函数都是对AutoreleasePoolPage的简单封装,所以自动释放机制的核心就在于这个类。

AutoreleasePoolPage

AutoreleasePoolPage是一个C++实现的类
AutoreleasePool并没有单独的结构,而是由若干个AutoreleasePoolPage以双向链表的形式组合而成(分别对应结构中的parent指针和child指针)。
AutoreleasePool是按线程一一对应的(结构中的thread指针指向当前线程)。
AutoreleasePoolPage每个对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了上面的实例变量所占空间,剩下的空间全部用来储存autorelease对象的地址。
上面的id *next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置。
一个AutoreleasePoolPage的空间被占满时,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象在新的page加入。
所以,向一个对象发送- autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置。

释放时刻

每当进行一次objc_autoreleasePoolPush调用时,runtime向当前的AutoreleasePoolPage中add进一个哨兵对象,值为0(也就是个nil),objc_autoreleasePoolPush的返回值正是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是:
1、根据传入的哨兵对象地址找到哨兵对象所处的page。
2、在当前page中,将晚于哨兵对象插入的所有autorelease对象都发送一次- release消息,并向回移动next指针到正确位置。
3、补充2:从最新加入的对象一直向前清理,可以向前跨越若干个page,直到哨兵所在的page

原文章地址:https://blog.csdn.net/linfengwenyou/article/details/47054205

2、ARC(Automatic Reference Counting)

ARC是指内存管理中对引用采取自动计数的计数。
在Objective-C中采用ARC机制是让编译器来进行内存管理。
满足ARC的条件:

  • 使用xcode 4.2或以上版本
  • 使用LLVM编译器3.0或以上版本
  • 编译器选项中设置ARC为有效(-fobjc-arc)

所有权修饰符

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

__strong
strong是id类型和对象类型的默认所有权修饰符。strong修饰符的变量obj在超出其变量作用域时,即在改变量被废弃时,会释放其被赋予的对象。
__weak
weak修饰符提供弱引用,自身不能持有引用对象,引用对象释放之后weak修饰的变量会自动设置为nil。
weak可以避免 strong修饰符中引发的循环引用问题,造成内存泄漏。
__unsafe_unretained
unsafe_unretained是不安全的修饰符,尽管ARC下的内存管理是编译器的工作,但unsafe_unretained修饰符的变量不属于编译器的内存管理对象。同weak一样,**unsafe_unretained不持有引用对象。
weak的区别是unsafe_unretained并不会在引用对象时自动设置为nil,所以在使用过程中需要手动设置为nil,否则会造成野指针问题。
__autoreleasing**
需要@autoreleasepool一起配合使用。

  1. @autoreleasepool {
  2. id __autoreleasing obj = [[NSObject alloc] init];
  3. }

访问附有weak修饰符的变量时,必定要访问注册到autoreleasepool的对象。**这是因为weak只持有对象的弱引用,而在访问引用对象的过程中,该对象有可能被废弃,如果要访问的对象注册到autoreleasepool中,那么在@autoreleasepool块结束之前都能确保该对象存在。
id的指针或对象的指针在没有显示指定会被附加上autoreleasing修饰符。例子:id *obj; `NSObject obj;等价于NSObject * autoreleasing obj; NSError *error;`出现过的例子方法:

  1. - (BOOL)performOperationWithError:(NSError * __autoreleaseing *)error;

ARC下遵循的规则:

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

主意的点:

  • dealloc中不能调用[super dealloc];否则会编译报错
  • C语言结构体中出现Objective-C对象,会出现编译错误

Core Foundation对象和Objective-C对象没有任何区别,区别只存在与来源不同的库,所以只需要C语言的转换就能实现相互转化,这种转换不需要使用额外的CPU资源,这种转换叫做Toll-Free Bridge

  1. CFMutableArrayRef cfObj = NULL;
  2. {
  3. id obj = [NSMutableArray array];
  4. cfObj = CFBridgingRetain(obj);
  5. CFShow(cfObj);
  6. printf("retain count = %d \n",CFGetRetainCount(cfObj));
  7. }
  8. printf("retain count after = %d \n",CFGetRetainCount(cfObj));
  9. CFRelease(cfObj);

输出结果:

retain count = 2 retain count after = 1

使用__bridge关键字转化

  1. id obj2 = (__bridge id)cfObj;

属性声明与所有权修饰符的关系

属性声明的属性 所有权修饰符
assin __unsafe_unretained
copy __strong
retain __strong
strong __strong
unsafe_unretained __unsafe_unretained
weak __weak

weak散列表
**
weak修饰的对象销毁过程中,最后会从weak散列表中查找销毁对象的地址为key的记录,并在weak表中删除该记录。并将__weak修饰的变量赋值为nil。

3、内存泄漏

Block造成内存泄漏的主要问题就是循环引用问题。
如下关系图:
常见避免循环引用的方式有实用weak修饰符,block 变量,__unsafe_unretained修饰符。

在使用__block 变量时优点:

  • 通过__block变量可控制对象的持有期间
  • 在不能使用weak修饰符的环境中不使用unsafe_unretained修饰符。在执行Block时可动态地决定是否将nil或其他对象赋值在__block变量中。

使用__block的缺点:

  • 为避免循环引用必须执行Block

使用__block变量时,如果没有执行Block依然会造成循环引用问题。