iOS深入理解property之strong和weak

前言

在上一篇文章是的时候,objc_setProperty方法的实现并没有体现strong和weak这两个修饰词,所以这两个修饰词是有另外的实现,而且是在上已层实现的;copy修饰词的话,只有copyWithZone和mutableCopyWithZone方法的调用,没有具体实现。

在看这么多的blog的时候,上过当,吃过亏。所以还是从源代码入手去更好理解这三个修饰词吧。

类的成员变量

分析strong和weak的实现之前,先看看对象的成员变量是怎么是进行赋值。

为什么呢?因为对象的属性的setter本质就是对对象的成员变量进行复制,一般情况下,每一个对象的属性就对应存在一个对象的成员变量。所以本质上即使在了解成员变量的strong和weak实现。

成员变量的getter

先看一下成员变量的结构体定义

//Ivar本质就是一个objc_ivar结构体,在objc_ivar结构体记录对象成员变量的信息
//这是在runtime.h头文件中的定义
typedef struct objc_ivar *Ivar;

struct objc_ivar {
    //变量名
    char * _Nullable ivar_name                               OBJC2_UNAVAILABLE;
    //变量的数据类型
    char * _Nullable ivar_type                               OBJC2_UNAVAILABLE;
    //变量在对象指针的偏移量,也就是变量存放在内存的实际位置
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}  
//这是在objc-class-old.mm文件中的定义,和上面的objc_ivar的机构体是一样的,只是名称不一样。
struct old_ivar {
    char *ivar_name;
    char *ivar_type;
    int ivar_offset;
#ifdef __LP64__
    int space;
#endif
};

//在objc-class-old.mm文件中的定义了这么几个宏
//应该是做了一个新旧结构体的映射
#define oldprotocol(proto) ((struct old_protocol *)proto)
#define oldmethod(meth) ((struct old_method *)meth)
#define oldcategory(cat) ((struct old_category *)cat)
#define oldivar(ivar) ((struct old_ivar *)ivar)
#define oldproperty(prop) ((struct old_property *)prop)

//在objc-class-old.mm文件中,这个函数是获取成员变量在对象的相对偏移量
ptrdiff_t ivar_getOffset(Ivar ivar) {
    return oldivar(ivar)->ivar_offset;
}
//这是在objc-runtime-new中定义的函数
ptrdiff_t ivar_getOffset(Ivar ivar) {
    if (!ivar) return 0;
    return *ivar->offset;
}

在获取成员变量,先了解上面成员变量的结构,可以看到在新旧系统中会有细微的差异,但是结构体的结构是一样的。

下面就是成员变量获取的实际实现了,下面就一句一句代码的分析里面的逻辑


/**
查找类成员变量的函数
cls:要查找的类
ivar:要查找的变量,通过变量的结构体对这个变量进行描述,包括变量名、变量类型和变量的指针便宜量
ivarOffset:变量相对于对象所在位置的指针偏移量
memoryManagement:内存管理信息
*/
static void 
_class_lookUpIvar(Class cls, Ivar ivar, ptrdiff_t& ivarOffset, 
                  objc_ivar_memory_management_t& memoryManagement)
{
    ivarOffset = ivar_getOffset(ivar);
    
    // Look for ARC variables and ARC-style weak.

    // Preflight the hasAutomaticIvars check
    // because _class_getClassForIvar() may need to take locks.
    //查找ARC成员变量和ARC内存管理的weak
    //提前检测是不是AutomaticIvars,其实就是是否开启ARC
    //
    bool hasAutomaticIvars = NO;
    for (Class c = cls; c; c = c->superclass) {
        if (c->hasAutomaticIvars()) {
            hasAutomaticIvars = YES;
            break;
        }
    }

    if (hasAutomaticIvars) {
        Class ivarCls = _class_getClassForIvar(cls, ivar);
        if (ivarCls->hasAutomaticIvars()) {
            // ARC layout bitmaps encode the class's own ivars only.
            // Use alignedInstanceStart() because unaligned bytes at the start
            // of this class's ivars are not represented in the layout bitmap.
            
            //ARC 只针对类自身的变量输出位图编码
            //使用alignedInstanceStart()是因为类的变量非位对齐其起始位置,并不代表在输出位图上
            ptrdiff_t localOffset = 
                ivarOffset - ivarCls->alignedInstanceStart();
            
            //查找成员变量的指针偏移量是否在类的变量输出位置
            if (isScanned(localOffset, class_getIvarLayout(ivarCls))) {
                //如果是,那么就是强引用的管理方式,因为属于类自身的变量,并返回
                memoryManagement = objc_ivar_memoryStrong;
                return;
            }
            //查找成员变量的指针偏移量是否在弱变量区域
            if (isScanned(localOffset, class_getWeakIvarLayout(ivarCls))) {
                //如果是,那么就是弱引用的管理方式,并返回
                memoryManagement = objc_ivar_memoryWeak;
                return;
            }

            // Unretained is only for true ARC classes.
            //这个类是ARC管理的其他情况就是Unretained的变量
            if (ivarCls->isARC()) {
                memoryManagement = objc_ivar_memoryUnretained;
                return;
            }
        }
    }
    //非ARC内存管理的,那么就赋值为未知内存管理方式
    memoryManagement = objc_ivar_memoryUnknown;
}

/**
获取对象的成员变量
obj:对象
ivar:成员变量的描述,包括成员变量的名称、类型和偏移量
*/
id object_getIvar(id obj, Ivar ivar)
{
    if (!obj  ||  !ivar  ||  obj->isTaggedPointer()) return nil;

    //成员变量的指针偏移量,用来存放查找偏移量结果
    ptrdiff_t offset;
    //成员变量的内存管理方式,用来存放查找变量对应的内存管理方式
    objc_ivar_memory_management_t memoryManagement;
    //通过对象的类,去查找成员变量,并且将偏移指针存放在offset,内存方式存放在memoryManagement
    _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
    //通过取到成员变量的偏移量,以对象为起始位置计算出其实际位置,获取对象指针
    id *location = (id *)((char *)obj + offset);

    //如果是弱应用
    if (memoryManagement == objc_ivar_memoryWeak) {
        //则在弱应用表中找出对应的对象
        return objc_loadWeak(location);
    } else {
        //否则,直接返回指向的对象
        return *location;
    }
}

通过以上这段代码,可以得到下面几点总结:

  • ARC内存管理是在这一层实现的

成员变量的setter

/**
对对象成员变量进行赋值,这是成员变量赋值在runtime层的实现
obj:需要赋值的对象
name:成员遍历那个的名称
value:性质
assumeStrong:需要设置的内存管理方式
*/
static ALWAYS_INLINE 
void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
{
    //空值判断
    if (!obj  ||  !ivar  ||  obj->isTaggedPointer()) return;

    ptrdiff_t offset;
    objc_ivar_memory_management_t memoryManagement;
    //获取对象成员变量的指针偏移量和内存管理方式
    _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);

    //如果内存管理方式为未知
    if (memoryManagement == objc_ivar_memoryUnknown) {
        //而且指定了strong强引用的,修正memoryManagement
        if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
        //没指定为strong强引用的,修正为Unretained
        else memoryManagement = objc_ivar_memoryUnretained;
    }
    
    //根据指针偏移量,获取成员变量指向的对象指针
    id *location = (id *)((char *)obj + offset);

    switch (memoryManagement) {
    //弱引用,则将新值存放在弱引用weak表
    case objc_ivar_memoryWeak:       objc_storeWeak(location, value); break;
    //强引用,则将新值存放在强引用的strong表
    case objc_ivar_memoryStrong:     objc_storeStrong(location, value); break;
    //Unretained的,则直接赋值
    case objc_ivar_memoryUnretained: *location = value; break;
    //这种情况不可能发生
    case objc_ivar_memoryUnknown:    _objc_fatal("impossible");
    }
}

//非strong默认的成员变量进行赋值
void object_setIvar(id obj, Ivar ivar, id value)
{
    return _object_setIvar(obj, ivar, value, false /*not strong default*/);
}
//对默认是strong的成员变量进行赋值
void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value)
{
    return _object_setIvar(obj, ivar, value, true /*strong default*/);
}

/**
对对象成员变量进行赋值,这是成员变量赋值在runtime层的实现
obj:需要赋值的对象
name:成员遍历那个的名称
value:性质
assumeStrong:需要设置的内存管理方式
*/
static ALWAYS_INLINE 
Ivar _object_setInstanceVariable(id obj, const char *name, void *value, 
                                 bool assumeStrong)
{
    Ivar ivar = nil;

    if (obj  &&  name  &&  !obj->isTaggedPointer()) {
        //通过给定的名字,查找成员变量
        if ((ivar = _class_getVariable(obj->ISA(), name))) {
            _object_setIvar(obj, ivar, (id)value, assumeStrong);
        }
    }
    return ivar;
}

//给对象成员变量进行复制
Ivar object_setInstanceVariable(id obj, const char *name, void *value)
{
    return _object_setInstanceVariable(obj, name, value, false);
}
//给对象用strong默认修饰的成员变量进行赋值
Ivar object_setInstanceVariableWithStrongDefault(id obj, const char *name, 
                                                 void *value)
{
    return _object_setInstanceVariable(obj, name, value, true);
}

由上面这段代码可以看出来,可以总结出来下面几点:

  • 所有对成员变量的赋值,都是通过调用_object_setIvar函数实现,_object_setIvar函数是具体实现,通过参数区分功能
  • 对象和对象的成员变量是放在一片连续的内存块上面的,在编译期在类中已经确定成员变量相对于对象的的位置
  • weak和strong修饰的成员变量,分别存放在不同的表中
  • Unretained修饰成员变量只做赋值操作

strong修饰词

在上面成员变量的setter上可以看到,对于用strong修饰的成员变量的赋值,是调用objc_storeStrong函数来实现的,那么现在看看objc_storeStrong的具体实现是怎样的

void
objc_storeStrong(id *location, id obj)
{
    //将*location指向的对象赋值给prev
    id prev = *location;
    //如果新值和原来的值一致,则返回,无需继续操作
    if (obj == prev) {
        return;
    }
    //对新值引用计数加一
    objc_retain(obj);
    //将对象指针指向新值
    *location = obj;
    //释放旧值
    objc_release(prev);
}

从源代码上看,对于strong修饰的成员变量操作比较简单,整个过程就是对新值引用计数加一,和对旧值进行释放

weak修饰词

strong修饰词的成员变量赋值比较简单,那么现在看看objc_storeWeak的源代码,看看objc_storeWeak是怎么工作的

/**
localtion : 对象指针
nebObj:新值
*/
static id 
storeWeak(id *location, objc_object *newObj)
{
    assert(haveOld  ||  haveNew);
    //根据haveNew参数判断是否有新值,如果有新值,而且新值指针指向的对象为空,则提示
    if (!haveNew) assert(newObj == nil);

    Class previouslyInitializedClass = nil;
    //旧值变量,用于存放旧值
    id oldObj;
    //旧值的weak引用表,用于记录该值的被引用情况
    SideTable *oldTable;
    //新值的weak引用表,用于记录新值的被引用情况
    SideTable *newTable;

    /**
    需要同时获取旧值和新值的锁
    为了解决锁定地址阻止锁的顺序问题
    如果旧值正在修改,那么跳回retry重新做一遍流程
    */
 retry:
    
    if (haveOld) {
        //如果haveOld的参数为真,那么根据location的对象指针获取旧值引用
        oldObj = *location;
        //则根据oldObj对象在全局的一个hash表获取SideTables类型的引用表
        oldTable = &SideTables()[oldObj];
    } else {
        //否则将旧引用表置空
        oldTable = nil;
    }
    if (haveNew) {
        //根据haveNew为真,则根据新值的对象在全局的一个hash表获取SideTables类型的引用表
        newTable = &SideTables()[newObj];
    } else {
        //否则将新引用表置空
        newTable = nil;
    }
    
    //同时对新引用表和旧引用表加锁
    SideTable::lockTwo(oldTable, newTable);
    
    //如果haveOld为真,而且location指针指向的对象并不是oldObj
    if (haveOld  &&  *location != oldObj) {
        //那么可能旧值已经更改,解锁两个表,然后重试
        SideTable::unlockTwo(oldTable, newTable);
        goto retry;
    }

    // Prevent a deadlock between the weak reference machinery
    // and the +initialize machinery by ensuring that no 
    // weakly-referenced object has an un-+initialized isa.
    /**
    通过保证没有一个弱引用的对象是没有一个没有initialized的isa
    来打断弱引用架构和+initialize架构间的死锁
    */
    if (haveNew  &&  newObj) {
        //获取新值对象的元类对象
        Class cls = newObj->getIsa();
        //如果新值的元类对象和之前初始化的不一致,且元类未初始化
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            //解锁两个表
            SideTable::unlockTwo(oldTable, newTable);
            //初始化新值对象的元类对象
            _class_initialize(_class_getNonMetaClass(cls, (id)newObj));

            // If this class is finished with +initialize then we're good.
            // If this class is still running +initialize on this thread 
            // (i.e. +initialize called storeWeak on an instance of itself)
            // then we may proceed but it will appear initializing and 
            // not yet initialized to the check above.
            // Instead set previouslyInitializedClass to recognize it on retry.
            previouslyInitializedClass = cls;
            //跳转到重试
            goto retry;
        }
    }

    if (haveOld) {
        //在旧值的weak表中清除旧值,如果weak表为空,同时在entry中删除weak表
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    if (haveNew) {
        /**
        将新值的weak表添加在entry中,并且将新值注册到weak表中
        weak_register_no_lock如果返回nil,则表示弱引用存放被拒绝了
        1.类中实现了allowsWeakReference,并且返回NO,表示不允许弱引用
        2.该对象正在调用deallocating进行释放
        */
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);

        //同时将是弱引用的标记写在对象的refcount table的表中
        if (newObj  &&  !newObj->isTaggedPointer()) {
            newObj->setWeaklyReferenced_nolock();
        }

        // Do not set *location anywhere else. That would introduce a race.
        //将location对象指针指向新值
        *location = (id)newObj;
    }
    else {
        //没有性质,什么都不做
    }
    //两个表同时解锁
    SideTable::unlockTwo(oldTable, newTable);
    //返回新值对象
    return (id)newObj;
}


/**
这个函数存放一个新值到一个使用__weak修饰的变量
可以在任何地方指定一个__weak变量
location:弱引用自身的内存地址
newObj:这个弱引用指针需要指向的值

返回: 返回新值
*/
id
objc_storeWeak(id *location, id newObj)
{
    return storeWeak
        (location, (objc_object *)newObj);
}

通过上面的分析,weak修饰的成员变量的赋值,比strong修饰的成员变量赋值得多了。可以总结一下:

  • objc_storeWeak函数不止是用来对对象成员变量的赋值,还包括所有使用__weak修饰符修饰的变量的赋值,以后还需要继续看回这个方法
  • 有一个全局的weak表存放在所有指向weak修饰的对象的指针的的指针,这个指针是该对象使用了weak修饰对象的引用的指针
  • 在对weak修饰的变量进行赋值的时候,会将旧值的weak表释放掉,并对创建新值的weak表,也就是说,在成员变量中大量使用weak变量的话,会一定程度上占用cpu的资源,会有效率损耗
  • 在赋值过成功有加锁和解锁过程,所以整个赋值过程是线程安全的

后记

在了解strong和weak修饰的成员赋值的过程,发现strong修饰的成员变量只是增加所持有对象的循环引用,而weak修饰的成员变量,则相对复杂很多。在了解过程中,可能还是会有理解偏差,需要多次阅读代码再补充。

还有一个就是要尽快弄好一个High Sierra来打断点调试真实的运行过程,现在只是在源代码上面的分析理解。

后记的后记

终于将系统降级到High Sierra了,通过objc的源代码进行调试,证明我的理解并没有太大的差异。

weak修饰的成员变量进行赋值的调用关系如下


iOS深入理解property之strong和weak_第1张图片
weak成员变量的调用栈

strong修饰的成员变量进行赋值的调用关系如下


iOS深入理解property之strong和weak_第2张图片
strong成员变量的调用栈

终于正式了自己测瞎猜,哈哈。开心!

你可能感兴趣的:(iOS深入理解property之strong和weak)