引言:
咱们都知道Objective-C经过“引用计数”来管理对象释放。基本原理就是管理对象的持有者个数(引用计数),引用计数为0时释放对象。如今有ARC(自动引用计数),则无需咱们本身显式持有(retain)和释放(release)对象,ARC经过对对像加上全部权修饰符(__strong等),编译器经过对象的全部权修饰符将会自动键入引用计数管理(根据全部权修饰符自动键入retain、release、autorelease)javascript
本文主要叙述引用计数的实现原理,ARC和MRC在使用上的区别,以及编译器在ARC中为咱们作了什么。java
使用栈(后进先出)来管理NSAutoreleasePool对象,所以能够随时拿到最近(hotPage)的NSAutoreleasePool,调用对象的autorelease方法,会在hotPage中的内部数组将对象加入进去。当NSAutoreleasePool出栈时,调用内部数组中元素的release方法便可。objective-c
引用计数表:使用散列表实现引用计数表,key为对象的地址的散列值,value为引用计数和内存块地址。
retain:经过对象的地址在引用计数表中找到引用计数,若是retainCount超过最大值,则抛异常,不然retainCount加1。
release:经过对象的地址在引用计数表中找到引用计数,若是retainCount值为0,则抛异常。不然retainCount减1,若是retainCount减1后为0,则从引用计数表冲移除,并调用对象的dealloc方法。express
Objective-C对象的ARC是经过全部权修饰符来管理对象的持有和释放。全部权修饰符一共有4种:数组
取得本身生成而且持有对象:
使用ARC:安全
{
id obj1 = [NSObject new];
//至关于
//id __strong obj1 = [NSObject new];
}复制代码
不使用ARC:框架
{
id obj1 = [NSObject new];
//在变量做用域结束时插入release
[obj1 release];
}复制代码
取得非本身生成并持有对象:
使用ARC:函数
- (void)create {
test = [self object];
NSLog(@"ARC------------------ARC");
NSLog(@"after create count = %ld", _objc_rootRetainCount(test));
}
- (id)object {
id obj = [[MyObject alloc] init];
return obj;
}
- (void)printRetainCount {
NSLog(@"ARC------------------ARC");
NSLog(@"retain count = %ld", _objc_rootRetainCount(test));
}复制代码
不使用ARC:优化
- (void)create {
if (test) {
NSLog(@"MRC------------------MRC");
NSLog(@"release");
[test release];
}
test = [self object];
[test retain];
NSLog(@"MRC------------------MRC");
NSLog(@"after create count = %ld", [test retainCount]);
}
- (id)object {
id obj = [[MyObject alloc] init];
[obj autorelease];
return obj;
}
- (void)printRetainCount {
NSLog(@"MRC------------------MRC");
NSLog(@"retain count = %ld", [test retainCount]);
}复制代码
打印结果:spa
**2016-12-13 15:35:47.545 ARCLearn[53676:16388070] ARC------------------ARC**
**2016-12-13 15:35:47.545 ARCLearn[53676:16388070] after create count = 1**
**2016-12-13 15:35:47.546 ARCLearn[53676:16388070] MRC------------------MRC**
**2016-12-13 15:35:47.546 ARCLearn[53676:16388070] after create count = 2**
**2016-12-13 15:35:49.602 ARCLearn[53676:16388070] ARC------------------ARC**
**2016-12-13 15:35:49.602 ARCLearn[53676:16388070] retain count = 1**
**2016-12-13 15:35:49.603 ARCLearn[53676:16388070] MRC------------------MRC**
**2016-12-13 15:35:49.603 ARCLearn[53676:16388070] retain count = 1**
**2016-12-13 15:35:51.212 ARCLearn[53676:16388070] myobject dealloc**
**2016-12-13 15:35:51.213 ARCLearn[53676:16388070] ARC------------------ARC**
**2016-12-13 15:35:51.214 ARCLearn[53676:16388070] after create count = 1**
**2016-12-13 15:35:51.214 ARCLearn[53676:16388070] MRC------------------MRC**
**2016-12-13 15:35:51.214 ARCLearn[53676:16388070] release**
**2016-12-13 15:35:51.215 ARCLearn[53676:16388070] myobject dealloc**
**2016-12-13 15:35:51.215 ARCLearn[53676:16388070] MRC------------------MRC**
**2016-12-13 15:35:51.215 ARCLearn[53676:16388070] after create count = 2**
**2016-12-13 15:36:03.540 ARCLearn[53676:16388070] ARC------------------ARC**
**2016-12-13 15:36:03.542 ARCLearn[53676:16388070] retain count = 1**
**2016-12-13 15:36:03.542 ARCLearn[53676:16388070] MRC------------------MRC**
**2016-12-13 15:36:03.543 ARCLearn[53676:16388070] retain count = 1**复制代码
能够看到使用ARC的对象持有,在返回对象时,并无把对象注册到autoreleasepool中,下面为ARC的autoreleasepool打印:
**objc[54013]: ##############**
**objc[54013]: AUTORELEASE POOLS for thread 0x10c6ba3c0**
**objc[54013]: 13 releases pending.**
**objc[54013]: [0x7fa3c9811000] ................ PAGE (hot) (cold)**
**objc[54013]: [0x7fa3c9811038] ################ POOL 0x7fa3c9811038**
**objc[54013]: [0x7fa3c9811040] 0x60800002e3c0 __NSCFString**
**objc[54013]: [0x7fa3c9811048] ################ POOL 0x7fa3c9811048**
**objc[54013]: [0x7fa3c9811050] 0x7fa3ca800850 UIScreen**
**objc[54013]: [0x7fa3c9811058] 0x7fa3ca800850 UIScreen**
**objc[54013]: [0x7fa3c9811060] 0x6000002712c0 __NSCFDictionary**
**objc[54013]: [0x7fa3c9811068] 0x7fa3c8600bf0 UIWindow**
**objc[54013]: [0x7fa3c9811070] 0x60000008e6f0 __NSMallocBlock__**
**objc[54013]: [0x7fa3c9811078] 0x6000000567d0 __NSSetM**
**objc[54013]: [0x7fa3c9811080] 0x600000052de0 __NSSetM**
**objc[54013]: [0x7fa3c9811088] 0x600000053b30 __NSSetM**
**objc[54013]: [0x7fa3c9811090] 0x600000271640 __NSCFString**
**objc[54013]: [0x7fa3c9811098] 0x600000271640 __NSCFString**
**objc[54013]: ##############**复制代码
下面为MRC的autoreleasepool打印:
**objc[54013]: ##############**
**objc[54013]: AUTORELEASE POOLS for thread 0x10c6ba3c0**
**objc[54013]: 14 releases pending.**
**objc[54013]: [0x7fa3c9811000] ................ PAGE (hot) (cold)**
**objc[54013]: [0x7fa3c9811038] ################ POOL 0x7fa3c9811038**
**objc[54013]: [0x7fa3c9811040] 0x60800002e3c0 __NSCFString**
**objc[54013]: [0x7fa3c9811048] ################ POOL 0x7fa3c9811048**
**objc[54013]: [0x7fa3c9811050] 0x7fa3ca800850 UIScreen**
**objc[54013]: [0x7fa3c9811058] 0x7fa3ca800850 UIScreen**
**objc[54013]: [0x7fa3c9811060] 0x6000002712c0 __NSCFDictionary**
**objc[54013]: [0x7fa3c9811068] 0x7fa3c8600bf0 UIWindow**
**objc[54013]: [0x7fa3c9811070] 0x60000008e6f0 __NSMallocBlock__**
**objc[54013]: [0x7fa3c9811078] 0x6000000567d0 __NSSetM**
**objc[54013]: [0x7fa3c9811080] 0x600000052de0 __NSSetM**
**objc[54013]: [0x7fa3c9811088] 0x600000053b30 __NSSetM**
**objc[54013]: [0x7fa3c9811090] 0x600000271640 __NSCFString**
**objc[54013]: [0x7fa3c9811098] 0x600000271640 __NSCFString**
**objc[54013]: [0x7fa3c98110a0] 0x608000017200 MyObject**
**objc[54013]: ##############**复制代码
在autoreleasepool里有MyObject对象。
为何ARC没有把对象放到autoreleasepool里了?它是怎样持有非本身生成的对象的?
代码:
{
id __strong obj = [NSMutableArray array];
}复制代码
转换为编译器模拟代码:
{
id obj = objc_msgSend(NSMutableArray, @selector(array));
objc_retainAutoreleasedReturnValue(obj);
objc_release(obj);
}复制代码
objc_retainAutoreleasedReturnValue函数,顾名思义:让obj持有(retain)池中(autoreleased)返回的值(retuenValue);
代码:
+ (id)array {
return [[NSMutableArray alloc] init];
}复制代码
转换为编译器模拟代码:
+ (id)array {
id obj = objc_msgSend(NSMutableArray, @selector(alloc));
objc_msgSend(obj, @selector(init));
return objc_autoreleaseReturnValue(obj);
}复制代码
objc_autoreleaseReturnValue函数,顾名思义:把obj注册在池中(调用obj的autorelease方法),并返回。
所以按照上面的理解objc_autoreleaseReturnValue
将返回对象注册到池子中,objc_retainAutoreleasedReturnValue`持有池中的对象。
可是事实不是那么简单:
可是objc_autoreleaseReturnValue远远不是autorelease那么简单。objc_autoreleaseReturnValue函数会检查使用该函数的方法或函数调用方的执行命令列表,若是方法或函数的调用方在调用了方法或函数后紧接着调用了objc_retainAutoreleasedReturnValue,那就不会将返回的对象注册到autoreleasepool中,而是直接传递到方法或函数的调用方。
所以objc_retainAutoreleasedReturnValue也不是retain那么简单,即便放回对象不注册到autoreleasepool中,也能正确的获取对象。
经过objc_autoreleaseReturnValue和objc_retainAutoreleasedReturnValue协做,能够不将对象注册到autoreleasepool中而直接传递,这一过程达到了最优化。
如今咱们添加一个weakTest变量,使用weak全部权修饰符
在ARC中实现:
@interface ARC() {
id test;
id __weak weakTest;
}
@end
@implementation ARC
- (void)create {
test = [self object];
weakTest = test;
_objc_autoreleasePoolPrint();
NSLog(@"ARC------------------ARC");
NSLog(@"after create count = %ld", _objc_rootRetainCount(test));
}
- (id)object {
id obj = [[MyObject alloc] init];
return obj;
}
- (void)printRetainCount {
NSLog(@"ARC------------------ARC");
NSLog(@"retain count = %ld", _objc_rootRetainCount(test));
}
@end复制代码
打印结果:
**2016-12-13 16:24:30.934 ARCLearn[55038:16489628] ARC------------------ARC**
**2016-12-13 16:24:30.935 ARCLearn[55038:16489628] after create count = 1**复制代码
由此能够看出__weak并无增长引用个数。
添加empty函数,使test为nil:
- (void)empty {
if (weakTest) {
NSLog(@"weak is %p", weakTest);
}
test = nil;
if (!weakTest) {
NSLog(@"weak change to nil");
}
}复制代码
打印结果:
**2016-12-13 16:33:06.301 ARCLearn[55349:16509066] weak is 0x608000011530**
**2016-12-13 16:33:06.301 ARCLearn[55349:16509066] myobject dealloc**
**2016-12-13 16:33:06.302 ARCLearn[55349:16509066] weak change to nil**复制代码
由此能够看出若附有__weak修饰符的变量所引用的对象被废弃,则将nil赋值给该变量。
是如何实现将nil值赋值给引用为0的__weak对象?
{
id __weak obj1 = obj;
}复制代码
转换为编译器模拟代码:
{
id obj1;
objc_initWeak(&obj1, obj);
objc_destroyWeak(obj1);
}复制代码
其中objc_initWeak(&obj1,obj)的实现:
obj1 = 0;
objc_storyWeak(&obj1, obj);复制代码
objc_destroyWeak的实现:
objc_storyWeak(&obj1, 0);复制代码
因此合在一块儿是:
{
id obj1;
obj1 = 0;
objc_storyWeak(&obj1, obj);
objc_storyWeak(&obj1, 0);
}复制代码
其中objc_storyWeak就是针对weak表(散列表)操做:
废弃引用为0的对象的流程:
将对象赋值给附有__autoreleasing修饰符的变量等同于ARC无效时调用对象的autorelease方法。
ARC有效:
@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
id __strong obj1 = [[NSObject alloc] init];
id __autoreleasing obj2 = obj1;
id __strong obj3 = [NSMutableArray array];
id __autoreleasing obj4 = obj3;
}复制代码
ARC无效:
@autoreleasepool {
id obj = [[NSObject alloc] init];
[obj autorelease];
id obj1 = [[NSObject alloc] init];
id obj2 = obj1;
[obj2 retain];
[obj2 autorelease];
id obj3 = [NSMutableArray array];
[obj3 retain];
id obj4 = obj3;
[obj4 retain];
[obj4 autorelease];
[obj3 release];
}复制代码
注意:显式的附加autoreleasing修饰符同显式地添加strong修饰符同样罕见。咱们常常非显式的使用__autoreleasing修饰符。
隐式使用__autoreleasing:
如:
NSError *error = nil;
BOOL result = [obj performOperationWithError: &error];复制代码
performOperationWithError声明为:
- (BOOL)performOperationWithError:(NSError **)error;复制代码
自动添加__autoreleasing的代码:
- (BOOL)performOperationWithError:(NSError * __autoreleasing *)error {
/* 错误发生 */
*error = [[NSError alloc] initwithDomain:MyAppDomain code:errorCode userInfo:nil];
return NO;
}复制代码
非ARC的实现:
- (BOOL)performOperationWithError:(NSError **)error {
/* 错误发生 */
*error = [[NSError alloc] initwithDomain:MyAppDomain code:errorCode userInfo:nil];
[*error autorelease];
return NO;
}复制代码
由于声明为NSError __autoreleasing 类型的error做为*error被赋值,因此可以返回注册到autoreleasepool中的对象。
注意:赋值给对象指针时,全部权修饰符必须一致。
编译器会自动加上全部权修饰符:NSError * __strong error = nil; NSError * __autoreleasing *pError = &error;复制代码
全部权修饰符不一致会产生编译错误:
Initializing 'NSError *__autoreleasing *' with an expression of type 'NSError *__strong *' changes retain/release properties of pointer
unsafe_unretained修饰符正如其名unsafe所示,是不安全的全部权修饰符。尽管ARC式的内存管理是编译器的工做,但附有unsafe_unretained修饰符的变量不属于编译器的内存管理对象。
ARC实现:
id obj = [[NSObject alloc] init];
id __unsafe_unretained obj1 = obj;复制代码
非ARC实现:
id obj = [[NSObject alloc] init];
id obj1 = obj;复制代码
因此就是...编译器啥都不作。这样当对象的引用计数为0,被废弃后,obj1就是不安全的了(悬垂指针),由于不会被置为nil。
注意:使用时确保对象确实存在。
何时使用__unsafe_unretained?
Core Foundation对象主要使用在用C语言编写的Core Foundation框架中,并使用引用计数的对象。
ARC无效时:
id obj = [[NSObject alloc] init];
void *p = obj; //void *至关与oc的id
id o = p;
[o release];复制代码
ARC有效时,会引发编译错误,要使用“__bridge”转换。
ARC有效时的代码:
id obj = [[NSObject alloc] init];
void *p = (__bridge void*)obj;
id o = (__bridge id)p;
[o release];复制代码
注意:bridge的安全性与赋值给unsafe_unretained修饰符相近,甚至会更低,若是管理时不注意赋值对象的全部者,就会因悬垂指针而致使程序崩溃。
__bridge转换中还有另两中转换,分别为:
id obj = [[NSObject alloc] init];
void *p = (__bridge_retained void*)obj;复制代码
ARC无效:id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];复制代码
void *p = 0;
id obj = (__bridge_transfer id)p;复制代码
ARC无效void *p = 0;
id obj = (id)p;
[obj retain];
[(id)p release];复制代码
简书我的主页:www.jianshu.com/users/b92ab…