• 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;
    }
  • 相关阅读:
    【内存管理】STL空间配置器
    【关键帧提取】基于CSD特征
    【内存管理】内存池技术
    【CUDA学习】计时方法
    【内存管理】简单的分配程序(续1)
    【背景建模】基于时空特征(续1)
    【OpenCV学习】准确定位帧
    【背景建模】基于时空特征(续2)
    【内存管理】简单的分配程序
    32位asp.net应用程序在64位Windows上运行时的问题一例
  • 原文地址:https://www.cnblogs.com/zhangzhang-y/p/13698470.html
Copyright © 2020-2023  润新知