__strong的实现
从内存管理的方法命名规则
的角度上将__strong对象的创建生成方式分为两种,分析其运行过程:
- 第一种:自己创建并持有
id __strong obj = [[NSArray alloc] init];
/* 编译器的模拟代码*/
id obj = obje msqSend (NSObject, @selector (alloc));
objc_msgSend (obj, @selector (init));
obic_release (obj);
- 第二种:非自己创建并持有,这种初始化方式,秉着谁创建谁释放的原则,返回值需要是一个autorelease对象才能配合调用方正确管理内存
id __strong obj = [NSArray array];
/* 编译器的模拟代码*/
id obj = objc_msgSend (NSMutableArray, @selector (array)); //返回一个autorelease对象
objc_retainAutoreleasedReturnValue (obj); //参数,应为autorelease对象。
那么array方法中到底做了什么,返回了一个autorelease对象
+(id) array
{
id obj = objc_msgSend (NSMutableArray, @selector (alloc));
objc_msgsend (objc, @selector(init));
return objc_autoreleaseReturnValue (obj);
}
要点:
- 内存管理方法命名规则规定:alloc/new/copy/mutableCopy开头之外的初始化方法需要返回autorelease对象
objc_retainAutoreleasedReturnValue
与objc_autoreleaseReturnValue
是成对出现的,用于alloc/new/copy/mutableCopy方法以外的初始化构造方法返回对象的实现上。- id类型与对象类型默认是__strong修饰符
# objc_autoreleaseReturnValue与objc_retainAutoreleasedReturnValue
两个函数的实现可以在 Objective-C NSObject.mm 的源码中找到:
//加工过的代码
id
objc_autoreleaseReturnValue(id obj)
{
if (callerAcceptsOptimizedReturn(__builtin_return_address(0))) {
if (ReturnAtPlus1){
tls_set_direct(RETURN_DISPOSITION_KEY, (void*)(uintptr_t)ReturnAtPlus1);
}
return obj;
}
return objc_autorelease(obj);
}
callerAcceptsOptimizedReturn(__builtin_return_address(0))函数在不同架构的 CPU 上实现也是不一样的。具体代码不再贴出来了
主要作用:
1、__builtin_return_address(0)获取当前函数返回地址。
2、callerAcceptsOptimizedReturn()方法判断调用方是否紧接着调用了 objc_retainAutoreleasedReturnValue或者 objc_unsafeClaimAutoreleasedReturnValue方法
3、如果调用了objc_retainAutoreleasedReturnValue,就表示外面是ARC环境,那么就可以使用TLS了,否则MRC就不能使用
id
objc_retainAutoreleasedReturnValue(id obj)
{
ReturnDisposition disposition = (ReturnDisposition)(uintptr_t)tls_get_direct(RETURN_DISPOSITION_KEY);
if (disposition == ReturnAtPlus1) return obj;
return objc_retain(obj);
}
补充说明:
-
objc_autoreleaseReturnValue
函数同objc_autorelease
函数不同,一般不仅限于注册对象到autoreleasepool中。 -
objc_autoreleaseReturnValue
函数会检查使用该数的方法或函数调用方的执行命令列表,如果方法或函数的调用方在调用了方法或函数后紧接着调用objc_retainAutoreleasedReturnValue()
函数(调用了这个方法就表示外面的环境是ARC),就将这个返回值obj储存在TLS[1]中,然后直接返回这个obj(不调用autorelease) - 同时,在外部接收这个返回值的objc_retainAutoreleasedReturnValue里,发现TLS[1]中正好存了这个对象,那么直接返回这个object(不调用retain)。
通过objc_autoreleaseReturnValue
函数和objc_retainAutoreleasedReturnValue
函数的协作,利用TLS[1]做中转,可以不将对象注册到autoreleasepool中而直接传递,免去了对返回值的内存管理,实现过程最优化
总结:
MRC下:对象需要经历方法内部new->内部autorelease->外部retain->外部release这样四步流程
ARC下:对象需要经历方法内部new->外部release两步,省了中间两步“autorelease->retain”
(TLS优化其实与OC内存管理“谁生成谁销毁谁持有谁释放”的黄金法则有所违背)
__weak的实现
# 要点:
- 不持有新值,不释放旧值
- 在持有某对象的弱引用时,当对象被废弃,弱引用自动失效,且置为nil
若使用附有__weak修饰符的变量,即是使用注册到autoreleasepool中的对象
- __weak修饰符的变量不能直接指向,没有强引用的、刚初始化完成的对象,因为没形成强引用,当即就会释放,所以会报警告。
- __weak修饰符只能用于iOS5以上以及OS X Lion以上的版本,在iOS 4以及OS X Snow Leopard的应用程序中可使用__unsafe_unretained修饰符来代替,有时在其他环境下也不能使用。
例如:
# 代码解析
下面通过一些代码来解析实现过程,注意,__weak是在objc ARC下编译的,所以转换成C++代码的时候,需要加一些指定环境
clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations test.m
{
id __weak obj1 = obj;
}
/*编译器的模拟代码*/
id obj1;
objc_initWeak (&obj1, obj); //通过objc-initWeak函数初始化附有__weak修饰符的变量
objc_destroyWeak (&obj1);//在变量作用域结束时通过objc_destroyWeak函数释放该变量.
objc_initWeak
与objc_destroyWeak
的源码实现:
id
objc_initWeak(id *location, id newObj)
{
if (!newObj) {
*location = nil;
return nil;
}
return storeWeak(location, (objc_object*)newObj);
}
objc_initWeak函数将附有__weak修饰符的变量初始化为0后,会将赋值的对象作为参数调用objc__storeWeak函数。
//objc_storeWeak (&obj1, obj);
void
objc_destroyWeak(id *location)
{
(void)storeWeak(location, nil);
}
objc_destroyWeak函数将0作为参数调用objc_storeWeak函数。
//objc_storeWeak(&obj1, 0);
即前面的源代码与下列源代码相同。
/*编译器的模拟代码*/
id obj1;
obj1 = 0;
objc_storeWeak (&obj1, obj);
objc_storeWeak (&obj1, 0);
# objc_storeWeak
objc_storeWeak
函数
把第二参数的赋值对象的地址作为键值
把第一参数的附有__weak修饰符的变量的地址注册到weak表中。
如果第二参数为0,则把变量的地址从weak表中删除。
weak表与引用计数表相同,作为散列表被实现
。如果使用weak表,将废弃对象的地址作为键值进行检索,就能高速地获取对应的附有__weak修饰符的变量的地址。另外,由于一个对象可同时赋值给多个附有 weak修饰符的变量中,所以对于一个键值,可注册多个变量的地址
。
# 释放对象的过程
(1) objc_release
(2) 因为引用计数为0所以执行dealloc
(3) _objc_rootDealloc
(4) obiect_dispose
(5) objc_destructInstance
(6) objc_clear_deallocating.
对象被废弃时最后调用的objc_clear_deallocating函数的动作如下:
(1)从weak表中获取废弃对象的地址为键值的记录。
(2)将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil.
(3)从weak表中删除该记录。
(4)从引用计数表中删除废弃对象的地址为键值的记录。
- 以上即是,__weak修饰符的变量所引用的对象被废弃时,被赋值为nil的过程
- 由以上也可知道,如果大量便用附有__weak修饰符的变量,则会消耗相应的CPU资源。良策是只在避免循环引用的时候使用__weak
__unsafe_unretained
- __unsafe_unretained是不安全的所有权修饰符,
ARC式的内存管理是编译器的工作
,但附有__unsafe_unretained修饰符的变量不属于编译器的内存管理对象。 - 其与_weak一样都是弱引用,区别在于__weak对象在释放的时候,对象或者指针会被置为nil,但是__unsafe_unretained不会,会造成野指针。