ARC
(Automatic Reference Counting
,自动引用计数) 是苹果公司在其编程语言(如 Objective-C 和 Swift)中的内存管理机制。ARC 通过编译器插入的代码自动管理对象的内存生命周期,减少了手动内存管理的复杂性和错误。
以下是 ARC 在编译期和运行期所做的工作:
// 示例代码
MyClass *obj = [[MyClass alloc] init]; // 引用计数为1
obj = nil; // 引用计数为0,触发dealloc释放内存
上面的代码,ARC会在编译时插入一下操作
MyClass *obj = [[MyClass alloc] init];
[obj retain]; // 增加引用计数
[obj release]; // 减少引用计数,当obj被赋值为null时
编译器会尝试优化引用计数的操作,合并或消除不必要的retain和release调用,例如,编译器会合并多个相邻的retain和release操作减少性能开销
MyClass *obj1 = [[MyClass alloc] init];
MyClass *obj2 = obj1;
在这种情况下,编译器知道ob2是obj1的别名,不需要增加引用计数
+ (instancetype)myObject {
return [[[self alloc] init] autorelease];
}
这里达到了延迟释放对象的效果,autorelease
把对象添加到当前的auto release池
中,使得对象在某个时刻(通常是当前事件循环结束时)自动释放,而不是立即释放。这种机制允许开发者创建的对象在返回调用者后依旧有效,不会立即释放。
- (void)dealloc {
[_name release];
[super dealloc];
}
在运行期,ARC会根据编译器插入的代码来管理对象的生命周期。
MyClass *obj = [[MyClass alloc] init]; // retainCount = 1
[obj retain]; // retainCount = 2
[obj release]; // retainCount = 1
[obj release]; // retainCount = 0, dealloc is called
@autoreleasepool {
MyClass *obj = [[[MyClass alloc] init] autorelease];
// obj will be released at the end of the autorelease pool block
}
3,解决循环引用
@interface MyClass : NSObject
@property (nonatomic, weak) id delegate;
@end
ARC
。编译器在代码里适当的地方自动插入 retain
/ release
完成内存管理(引用计数)。但ARC相对于MRC,又不是在编译时添加retain/release/autorelease这么简单。应该是编译期和运行期两部分共同帮助开发者管理内存。ARC
用的是更低层C接口实现的retain/release/autorelease
,并不通过OC
的消息转发机制,而是直接调用其底层C语言版本API,这样做的性能更好,因为保留及释放操作需要频繁的执行,直接调用其底层的函数能节省很多CPU周期。release
;类中的对象,在dealloc
方法中释放。runloop
时,都会检查对象的retainCount,如果retaincount = 0时,说明该对象没有地方要继续使用了,可以释放掉了。补充:
对于__unsafe_unretained修饰符,__unsafe_unretained 和 __weak 相似,是一种弱引用关系。区别在于如果一个对象没有强指针引用,则 __unsafe_unretained 引用不会被置为 nil,而是会变成一个野指针。
那有了 __weak,为什么还有 __unsafe_unretained 呢?
__unsafe_unretained 主要是跟 C 语言代码相互。此外,__weak 会消耗一定的性能,使用 __weak 需要检查对象是否被释放,在追踪是否被释放的时候需要追踪一些信息,则使用 __unsafe_unretained 比 __weak 快,消耗 CPU 资源也比 __weak 少。
而且一个对象有大量的 __weak 引用对象的时候,当对象被释放,那么此时就要遍历 weak 表,把表里所有的指针置空,消耗 CPU 资源。
综上所述,当明确知道对象的生命期时,选择 __unsafe_unretained 会有一些性能提升。但是 __unsafe_unretained 也容易引发野指针问题。
自动释放池
1,自动释放池底层怎么实现?
内存里面有栈,栈里面有自动释放池。自动释放池以栈的形式实现:当你创建一个新的自动释放池时,它将被添加到栈顶。当一个对象收到发送autorelease消息时,它被添加到当前线程的处于栈顶的自动释放池中,当自动释放池被回收时,它们从栈中被删除,并且会给池子里面所有的对象都会做一次release操作。
在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
2,自动释放池作用
将对象与自动释放池建立关系,池子内调用 autorelease 方法,在自动释放池销毁时销毁对象,延迟 release 销毁时间
3,苹果是如何实现 autoreleasepool 的?
autoreleasepool 以一个队列数组的形式实现,主要通过下列三个函数形成 objc_autoreleasepoolPush、objc_autoreleasepoolPop、objc_autorelease。
前两个函数执行 autorelease 的 push 和 pop 操作,销毁对象执行 release 操作。