学习准备

Objective-C内存篇(二) - 所有权修饰符__str

2018-08-13  本文已影响213人  Tenloy

__strong的实现

内存管理的方法命名规则的角度上将__strong对象的创建生成方式分为两种,分析其运行过程:

id __strong obj = [[NSArray alloc] init]; 
/* 编译器的模拟代码*/
id obj = obje msqSend (NSObject, @selector (alloc));
objc_msgSend (obj, @selector (init));
obic_release (obj);
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);
}

要点:

# 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_retainAutoreleasedReturnValue函数的协作,利用TLS[1]做中转,可以不将对象注册到autoreleasepool中而直接传递,免去了对返回值的内存管理,实现过程最优化

总结:
MRC下:对象需要经历方法内部new->内部autorelease->外部retain->外部release这样四步流程
ARC下:对象需要经历方法内部new->外部release两步,省了中间两步“autorelease->retain”
(TLS优化其实与OC内存管理“谁生成谁销毁谁持有谁释放”的黄金法则有所违背)

__weak的实现

# 要点:

例如:

  

# 代码解析

下面通过一些代码来解析实现过程,注意,__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_initWeakobjc_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)从引用计数表中删除废弃对象的地址为键值的记录。

__unsafe_unretained


  1. TLS 全称为 Thread Local Storage(线程本地存储),是每个线程专有的键值存储,需要调用方与被调用方必须都是ARC的情况下(即全ARC环境下)

上一篇 下一篇

猜你喜欢

热点阅读