5.OC底层-成员变量、属性、方法-探究

282 阅读2分钟
struct class_rw_t {
    ...
    ...
    //成员变量获取方法
    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is<class_rw_ext_t *>())) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
        }
        return v.get<const class_ro_t *>(&ro_or_rw_ext);
    }
   ....
   //方法获取方法
    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
        } else {
            return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
        }
    }
    //属性获取方法
    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
        } else {
            return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
        }
    }
    //协议获取方法
    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is<class_rw_ext_t *>()) {
            return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
        } else {
            return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
        }
    }
};

写一个基本类

  • 有属性、成员变量、实例变量 image.png
  • 文件.m -> .cpp 文件指令
    • clang -rewrite-objc main.m -o main.cpp
  • 属性通过编译之后会变成带“_”的成员变量,同时会自动生成响应的set和get方法 image.png
static NSString * _I_LGPerson_nickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nickName)); }

static void _I_LGPerson_setNickName_(LGPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _nickName), (id)nickName, 0, 1); }

static NSString * _I_LGPerson_nnickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)); }
static void _I_LGPerson_setNnickName_(LGPerson * self, SEL _cmd, NSString *nnickName) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nnickName)) = nnickName; }

  • get:是通过指针的偏移获取获取值得
  • set: 是调用了 objc_setProperty 方法完成、或者内存偏移赋值完成。

类型与编码

- 在我们找方法中最后打印了一个 type "@16@0:8"
(lldb) p $6.get(0).big()
(method_t::big) $7 = {
  name = "hobby"
  types = 0x0000000100003f53 "@16@0:8"
  imp = 0x0000000100003dc0 (KCObjcBuild`-[LGPerson hobby])
}
  • - (NSString *)hobby; 这个方法的编码 为 "@16@0:8"

  • 编码类型转化

    • @-> id 指针 刚好对应了 (NSString *)
    • 方法中默认的有两个隐形参数 (LGPerson * self, SEL _cmd)
    • LGPerson * 对应了第二个 @
    • SEL 对应了 :
  • 所以如果将方法写全了应该是

    • (NSString *)hobby:(LGPerson *)self sel:(SEL)cmd
  • 16 : 表示这个方法,参数所占用总内存大小

  • 0 : 表示第一个参数从0位置开始

  • 8 : 表示第二个参数从8位置开始 image.png

  • - (void)setNickName:(NSString *)nickName

  • {(struct objc_selector *)"setNickName:", "v24@0:8@16", (void *)_I_LGPerson_setNickName_},

    • v - void
    • @ - LGPerson *
    • : - SEL
    • @ - NSString *
    • 占用 24字节
    • self 0 开始
    • cmd 8 开始
    • nickName 16 开始
  • setName -> objc_setProperty , objc_setProperty是set方法的抽取封装的基类的方法。

  • 利用的时依赖倒置原则的设计模式完成的对set方法的封装 image.png

加载流程

  • 1
      1. 编译的时候在加载 ivars 成员变量时,利用 SEL ——> imp 设置方法的地址。(未实现)
      1. 调用的时候调用 objc_setProperty,将这个地址重定向一个真正实现方法的地址
        void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) 
    {
        bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
        bool mutableCopy = (shouldCopy == MUTABLE_COPY);
        reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
    }
    ------------------
    - 对旧值和新值得处理,以及对内存空间的处理。
    static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
    {
        if (offset == 0) {
            object_setClass(self, newValue);
            return;
        }
    
        id oldValue;
        id *slot = (id*) ((char*)self + offset);
    
        if (copy) {
            newValue = [newValue copyWithZone:nil];
        } else if (mutableCopy) {
            newValue = [newValue mutableCopyWithZone:nil];
        } else {
            if (*slot == newValue) return;
            newValue = objc_retain(newValue);
        }
    
        if (!atomic) {
            oldValue = *slot;
            *slot = newValue;
        } else {
            spinlock_t& slotlock = PropertyLocks[slot];
            slotlock.lock();
            oldValue = *slot;
            *slot = newValue;        
            slotlock.unlock();
        }
    
        objc_release(oldValue);
    }
    
    
    
    
  • LLVM 编译 getSetPropertyFn() 创建一个 objc_setProperty 方法 image.png

image.png

  • GetPropertySetFunction -> getSetPropertyFn

image.png

  • case GetSetProperty 或 case PropertyImplStrategy::SetPropertyAndExpressionGet: { image.png

  • switch (strategy.getKind()) 由 stategy.getKind() image.png

  • 找到这个类型的赋值的地方

image.png

  • 找到初始化方法 image.png

image.png

  • 结论是copy

代码验证

image.png

image.png

copy的补充