[iOS]2、类的原理

1、class的底层结构探究

通过如下代码进行 class 的结构调试

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        NSLog(@"Person");
    }
    return 0;
}
// Person.h
@interface Person : NSObject {
    NSString *subject;
}
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *hobby;
- (void)sayNB;
+ (void)say666;
@end
// Person.m
@implementation Person
- (void)sayNB {}
+ (void)say666 {}
@end

补充:
1、objc_class 继承自 objc_object
2、pobjc_class 对象,Person 对象为 objc_object结构体
3、idobjc_object* 指针
4、classobjc_object的指针地址

1.1、objc_class 继承自 objc_object
struct objc_object {
private:
    isa_t isa;
}

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;
}

从类的结构中分析得知,前 8 个字节为 isa, 后 8 个字节为 superclass
控制台操作:
x/4gx Person.class Person 元类的内存结构

0x100008380: 0x00000001000083a8 0x000000010036e140
0x100008390: 0x000000010066c070 0x0002802800000003

po 0x00000001000083a8 第一个 8 个字节(isa)打印结果

Person

po 0x000000010036e140 第二个 8个字节(superclass)打印结果

NSObject
1.2、cache_t

cache_t 的结构体如下

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;  // 4
#if __LP64__
            uint16_t                   _flags; // 2
#endif
            uint16_t                   _occupied; // 2
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; // 8
    };
}
struct explicit_atomic : public std::atomic<T> {
}

explicit_atomic 为泛型结构,大小为泛型的类型
cache_t 结构体的大小为 16 个字节

1.3、class_data_bits_t
struct class_data_bits_t {
    friend objc_class;
    // Values are the FAST_ flags above.
    uintptr_t bits;
public:
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
}

综上 class_data_bits_t 位于 objc_class 的第 36 个字节处
p/x 0x100008380+0x20

(long) $7 = 0x00000001000083a0

p (class_data_bits_t *)0x00000001000083a0

(class_data_bits_t *) $8 = 0x00000001000083a0

p $8->data() 查看 class_data_bits_t 里面的 data() 内容

(class_rw_t *) $10 = 0x000000010066c030

p *$10 查看 class_rw_t 的内容

(class_rw_t) $11 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295000344
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff80103a88
}
1.4、以下是通过 class_rw_t 的源码来查找类的属 性内容

class_rw_t 部分源码

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif
    explicit_atomic<uintptr_t> ro_or_rw_ext;
    Class firstSubclass;
    Class nextSiblingClass;

    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};
        }
    }
}

p $11.properties() 查看 properties 属性

(const property_array_t) $12 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008260
      }
      arrayAndFlag = 4295000672
    }
  }
}

p $12.list 查看 list里面的迭代器内容

(const RawPtr<property_list_t>) $13 = {
  ptr = 0x0000000100008260
}

p $13.ptr 查看 ptr 地址

(property_list_t *const) $14 = 0x0000000100008260

p *$14 查看 ptr 的内部结构

(property_list_t) $15 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}

p $15.get(0) 调用 get 方法获取属性值

(property_t) $16 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
1.5、属性

探究:属性列表存在于结构体 class_ro_t

x/4gx Person.class 查看 Person 的元类信息

0x100008408: 0x0000000100008430 0x000000010036e140
0x100008418: 0x0000000100662570 0x0002802800000003

p/x 0x100008408+0x20 查看 class_data_bits_t 结构体

(long) $1 = 0x0000000100008428

p (class_data_bits_t *)0x0000000100008428

(class_data_bits_t *) $2 = 0x0000000100008428

p $2->safe_ro()

(const class_ro_t *) $3 = 0x0000000100008188

查看源码 class_ro_t 结构体

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];
}

p *$3 查看 class_ro_t 结构体

(const class_ro_t) $4 = {
  flags = 0
  instanceStart = 8
  instanceSize = 32
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "LGPerson" {
      Value = 0x0000000100003f4d "LGPerson"
    }
  }
  baseMethodList = 0x00000001000081d0
  baseProtocols = nil
  ivars = 0x0000000100008268
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000082d0
  _swiftMetadataInitializer_NEVER_USE = {}
}

p $3->ivars 查看 Person 类的属性

(const ivar_list_t *const) $5 = 0x0000000100008268

p *$5 查看变量的具体信息

(const ivar_list_t) $6 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}

p $6.get(0) 查看属性

(ivar_t) $7 = {
  offset = 0x00000001000083a0
  name = 0x0000000100003e11 "hobby"
  type = 0x0000000100003f56 "@\"NSString\""
  alignment_raw = 3
  size = 8
}

2、内存平移

// main.m
@interface Person : NSObject
{
    NSString *hobby;
    NSObject *objc;
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;
@property (atomic, strong) NSString *aname;
+ (void)test;
- (void)test;
@end

@implementation Person
+ (void)test {}
- (void)test {}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc]init];
        p.name = @"KC";
        p.nickName = @"老师";
        p.aname = @"aNNN";
    }
    return 0;
}

以上是 main.m 的具体类结构
使用命令编译 main.m 文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
编译后的属性部分源码

static NSString * _I_Person_nickName(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

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

static NSString * _I_Person_name(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)); }
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)) = name; }

static NSString * _I_Person_aname(Person * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_aname)); }
static void _I_Person_setAname_(Person * self, SEL _cmd, NSString *aname) { (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_aname)) = aname; }
  • 具有 copy 的属性,编译后的结果中执行 objc_setProperty 方法

查看objc 源码

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);
}

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);
}

void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}

void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, false, false, false);
}


void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, true, true, false);
}

void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset)
{
    reallySetProperty(self, _cmd, newValue, offset, false, true, false);
}

最终调用 copyWithZone 方法

  • 没有 copy 的属性,直接执行内存平移

比如 (*(NSString **)((char *)self + OBJC_IVAR_$_Person$_name)) = name;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容