iOS—strong、weak的实现以及dealloc的调用流程

本文深入探讨了iOS中__strong和__weak修饰符的实现原理,包括storeStrong和storeWeak操作,以及ARC在编译器和运行期的工作。详细分析了__weak变量在内存管理中的行为,如objc_initWeak、objc_destroyWeak等函数的调用,以及dealloc调用流程,包括_objc_rootDealloc、objc_destructInstance和clearDeallocating等步骤。文章还解释了如何在对象被释放时,weak指针自动设为nil的过程。
摘要由CSDN通过智能技术生成

__strong修饰符

在这里插入图片描述

命令行使用下面的命令来将 Objective-C 代码转成 LLVM 中间码
clang -S -fobjc-arc -emit-llvm main.m -o main.ll
LLVM中间码如下:

define i32 @main(i32 %0, i8** %1) #0 {
   
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  %5 = alloca i8**, align 8
  %6 = alloca i8*, align 8
  %7 = alloca i8*, align 8
  store i32 0, i32* %3, align 4
  store i32 %0, i32* %4, align 4
  store i8** %1, i8*** %5, align 8
  %8 = call i8* @llvm.objc.autoreleasePoolPush() #1
  %9 = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_", align 8
  %10 = bitcast %struct._class_t* %9 to i8*
  
  %11 = call i8* @objc_opt_new(i8* %10)
  
  %12 = bitcast i8* %11 to %0*
  %13 = bitcast %0* %12 to i8*
  store i8* %13, i8** %6, align 8
  %14 = load i8*, i8** %6, align 8
  
  %15 = call i8* @llvm.objc.retain(i8* %14) #1
  
  store i8* %15, i8** %7, align 8
  
  call void @llvm.objc.storeStrong(i8** %7, i8* null) #1
  call void @llvm.objc.storeStrong(i8** %6, i8* null) #1
  
  call void @llvm.objc.autoreleasePoolPop(i8* %8)
  ret i32 0
}

简化如下:

  %11 = call i8* @objc_opt_new(i8* %10)        //创建对象
  %15 = call i8* @llvm.objc.retain(i8* %14) #1  //引用计数+1
  call void @llvm.objc.storeStrong(i8** %7, i8* null) #1  //release
  call void @llvm.objc.storeStrong(i8** %6, i8* null) #1  //release

对于storeStrong,查看源码实现如下:
在这里插入图片描述
location:指针的地址
obj:指向对象的指针,存储对象的地址
逻辑可以理解为

  • 检查参数obj与指针*location存储的地址是否相同
  • 若相同,直接返回
  • 若不同
    • 对新的对象obj引用计数+1,当前 *location指向obj
    • 对原来指向的对象引用计数-1

简化后的中间码storeStrong第二个参数为null,就相当于是对对象进行了release操作。

ARC在编译器、运行期做了哪些工作?

编译期:根据代码执行的上下文语境,在适当的位置插入retain,release

运行期:

  • __weak修饰的变量能够在其指向的对象引用计数为0时,自动设置为nil。
  • ARC可以在运行期检测出autorelease及紧跟其后的retain,此时不直接掉用autorelease方法,而是改为调用objc_autoreleaseReturnValue函数。这个函数会检查当前方法返回之后的即将要执行的代码。如果在返回的对象上执行retain操作,则设置全局数据结构中的一个标志位,而不执行autorelease操作。
    与之相似,如果方法返回了一个自动释放的对象,此时不直接执行retain,而是改为执行objc_retainAutoreleaseReturnValue函数,这个函数会检测刚提到的标志位,如果已经置位,则不执行retain。
    设置并检测标志位,要比调用autorelease和retain快。

内存管理语义在方法名中表现出来
alloc、new、copy、mutableCopy开头的方法,其返回的对象归调用者所有
类似于array这种非上述四种命名方式并返回对象的方法,其返回的对象并不归调用者所有,会调用autorelease方法,以确保可以多存活一段时间。
若紧跟其后会对该对象进行retain操作,那么这一对操作就是多余的,为了提高性能,可将二者删去,但又考虑到需要兼容不实用ARC的代码,所以上述的优化保证了向后兼容性


__weak修饰符

__weak在使用过程中调用的函数

在这里插入图片描述
汇编语言如下
在这里插入图片描述
实际调用如下
objc_alloc_init
objc_initWeak
objc_release
objc_destroyWeak

首先创建并初始化对象,通过objc_initWeak函数初始化附有__weak修饰符的变量,由于弱引用,该指针变量不持有对象,所以对象会立即释放,执行objc_release,最后调用objc_destroyWeak释放附有__weak修饰符的变量。


在这里插入图片描述
汇编:
在这里插入图片描述
实际调用:
objc_alloc_init
objc_initWeak
objc_destroyWeak
objc_storeStrong

与上述过程差不太多


在这里插入图片描述
汇编:
在这里插入图片描述
实际调用:
objc_alloc_init
objc_initWeak
objc_loadWeakRetained
NSLog
objc_release
objc_destroyWeak
objc_storeStrong

与之前不同的是,在打印__weak修饰的obj1前,调用了objc_loadWeakRetained,打印完后调用了objc_release。


objc_initWeak

objc_initWeak源码如下

id objc_initWeak(id *location, id newObj)
{
   
    if (!newObj) {
   
        *location = nil;
        return nil;
    }

    return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
        (location, (objc_object*)newObj);
}

该方法有两个参数location、newObj

  • location:存储__weak指针的地址(即指针的指针),可通过*location对__weak指针进行修改赋值
  • newObj:所引用的对象

通过源码可知,所引用的对象为nil则直接返回nil
真正的实现是storeWeak

storeWeak实现如下

storeWeak


// Template parameters.

enum HaveOld {
    DontHaveOld = false, DoHaveOld = true };  //是否有旧值
enum HaveNew {
    DontHaveNew = false, DoHaveNew = true };  //是否有新值

//操作正在释放中的对象是否会crash
enum CrashIfDeallocating {
   
    DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
};

template <HaveOld haveOld, HaveNew haveNew,
          enum CrashIfDeallocating crashIfDeallocating>
          
static id 
storeWeak(id *location, objc_object *newObj)
{
   
    ASSERT(haveOld  ||  haveNew);
    if (!haveNew) ASSERT(newObj == nil);

    Class previouslyInitializedClass = nil;
    id oldObj;
    SideTable *oldTable;
    SideTable *newTable;

    // Acquire locks for old and new values.
    // Order by lock address to prevent lock ordering problems. 
    // Retry if the old value changes underneath us.
 retry:
 
    if (haveOld) {
      
    	//如果__weak指针之前弱引用过一个obj,则将这个obj对应的SideTable取出,赋值给oldTable
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
            
    	//如果__weak指针之前没有弱引用过一个obj,则oldTable = nil
        oldTable = nil;
    }
    
    if (haveNew) {
      //如果__weak指针要弱引用一个新的obj,则将obj对应的sideTable从sidetables中取出,赋值给newTable
        newTable = &SideTables()[newObj];
    } else {
   
        newTable = nil;  //如果__weak指针不需要引用一个新obj,则newtbale为nil
    }

	//加锁操作,防止多线程中竞争冲突
    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

	//location应该与oldObj保持一致,如果不同,说明当前location已经处理过oldObj可是又被其他线程所修改
    if (haveOld  &&  *lo
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值