OC 底层探索 05、属性、成员、实例变量简析

本文探索属性、成员、实例变量。

一、简介

1、属性变量

@property (nonatomic, copy) NSString *propName;

@property:做了什么事?文章后面探索。

为 propName 生成了 set/get 方法的声明和实现,同时为其生成了私有的 ivar 成员变量  _propName;

2、成员变量

@interface MyPerson : NSObject {
   
    NSString *instName;
}

没有 set/get,只有私有的 _instName 变量。

3、实例变量

可以进行实例化的对象 --> 实例变量是一种特殊的成员变量

@interface MyPerson : NSObject {
   
    NSObject *obj;
    MyPerson *person1;
    UIButton *btn;
}

二、详解 

通过 .cpp 文件和源码进行分析

之前文章 对象本质与isa 已介绍过通过 clang 编译后cpp文件,main.m文件编译后内容如下图(main.m内容见本文底)。

1、变量和方法

1、成员变量:

从 .cpp 文件可以看到,struct MYPerson_IMPL 中,可以看到我们声明的成员变量 privName 和 property 声明的2个变量编译成 _xxxx 的形式的成员变量。

2、sel imp

111650 行 “MYPerson * self, SEL _cmd”,每个方法都是默认有这两个属性的 --> 引申 SEL IMP 

SEL:方法编号 --> 我们定义的方法

IMP:函数实现指针 --> 指向方法实现的指针

2、ivar_getTypeEncoding

代码中“@ v :”这些符号是什么含义呢?方法签名

示例:v16@0:8 -->

      v ->  返回值类型:void 即无返回值

      16 -> 共申请开辟的内存:16 字节

      @ -> 第一个参数类型:id

      0 -> 第一个参数位置从第几位开始:从 0 位值开始,占 8 字节

      :-> 方法

      8 -> 方法 位置从第几位开始:从第 8 位开始,到第 15 位,也占了8字节。

苹果文档:Type Encodings 

3、property 的 copy 和 strong

从 .cpp 文件的 111655 ~ 111661 行,是两个 NSString 类型的属性变量的编译后代码,区别是 cNickName 用copy 修饰,sNickName 用 strong 修饰。我们发现2者的 set 方法是不同的。

set 操作

copy:--> objc_setProperty() --> reallySetProperty() --> 新值 retain  旧值 release 

 

strong

源码 --> objc_storeStrong() --> 简单的新旧值 retain release

.cpp 底层编译:

// strong 的 set 方法
static void _I_MYPerson_setSNickName_(MYPerson * self, SEL _cmd, NSString *sNickName) { 
    (*(NSString **)((char *)self + OBJC_IVAR_$_MYPerson$_sNickName)) = sNickName; 
}

C++ 中是没有 retain release 的,如上代码,(char *)self 位置进行地址平移,OBJC_IVAR_MYPerson_sNickName 的位置,到 sNickName 的位置(这个位置我们是不知道的不必细究),然后在此位置进行赋值操作。其实就是做了内存平移然后赋值。

和上面源码一致,location 即 sNickName 的地址,拿到地址上的值,判断新值旧值是否相同,相同则直接 return。

get 操作

static NSString * _I_MYPerson_cNickName(MYPerson * self, SEL _cmd) { 
    return (*(NSString **)((char *)self + OBJC_IVAR_$_MYPerson$_cNickName)); 
}

get 操作比较简单,就是通过指针平移找到位置取出值。 

weak 扩展

weak:objc_storeWeak() --> storeWeak() --> SideTable 散列表(weak_table) --> 新旧值处理:

首先 针对 有旧值时, 一直对散列表进行 循环,取值、判断 ,直至找到旧值的位置 --> 对新值进行判断处理 -->  清除旧值 --> 注册新值 --> 新值赋在 location 处 --> 最后返回新值

源码:

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) {
        oldObj = *location;
        oldTable = &SideTables()[oldObj];
    } else {
        oldTable = nil;
    }
    if (haveNew) {
        newTable = &SideTables()[newObj];
    } else {
        newTable = nil;
    }

    SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);

    // 有旧值时,不断循环查找 --  直到 找到 location 上的旧值
    if (haveOld  &&  *location != oldObj) {
        SideTable::unlockTwo<haveOld, haveNew>(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.
    // 有新值
    if (haveNew  &&  newObj) {
        Class cls = newObj->getIsa();
        // 新值为全新的
        if (cls != previouslyInitializedClass  &&  
            !((objc_class *)cls)->isInitialized()) 
        {
            SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
            class_initialize(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  - 继续 retry
            previouslyInitializedClass = cls;

            goto retry;
        }
    }

    // Clean up old value, if any.
    // 清除 旧值
    if (haveOld) {
        weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
    }

    // Assign new value, if any.
    // 注册新值
    if (haveNew) {
        newObj = (objc_object *)
            weak_register_no_lock(&newTable->weak_table, (id)newObj, location, 
                                  crashIfDeallocating);
        // weak_register_no_lock returns nil if weak store should be rejected

        // Set is-weakly-referenced bit in 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 {
        // No new value. The storage is not changed.
    }
    
    SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
    // 返回新值
    return (id)newObj;
}

 

main.m 文件内容

#import <Foundation/Foundation.h>

@interface MYPerson : NSObject {
    
    NSString *privName;
}

@property (nonatomic, copy) NSString *cNickName;
@property (nonatomic, strong) NSString *sNickName;

- (void)funcTest;
@end

@implementation MYPerson
- (void)funcTest {
    
}
@end


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

 

posted @ 2020-09-20 23:16  张张_z  阅读(300)  评论(0编辑  收藏  举报