ios 类的结构分析

1、首先抛出问题   类的结构是什么?

首先创建一个LGPesron的类,去cpp文件中。

利用clang编译成cpp源码(先cd到当前文件的目录):

```

clang -rewrite-objc main.m -o main.cpp

存在UIKit等其他动态引用库时:

clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot/Application/Xcode.app/Comtents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m

xcrun xcode 命令

模拟器:xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

真机:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

```

打开cpp文件中全局搜索LGPerson,可以看到

typedef struct objc_object LGPerson;

struct objc_class {

    Class_Nonnullisa__attribute__((deprecated));

}__attribute__((unavailable));

在搜索objc_class ,可以看到

typedef struct objc_class *Class;

以上可以看到Class的真正类型为objc_class。

通过上面的探索,我们可以看到类是一种结构体,来自于我们的底层编译  objc_class  *Class,类就是Class.  往下层走我们可以看到objc_class的结构体,这个结构体来自于一个继承objc_object,这个结构体里面我们可以看到类的几个属性:

// Class ISA; // 8

    Class superclass;// 8

    cache_t cache;    // 16 不是8        // formerly cache pointer and vtable

    class_data_bits_t bits;

2、我们通常都是用类定义一些东西:属性和方法,那这些属性和方法的定义在哪找到?怎么去找?

在上面我们已经知道了类里面有Class ISA; Class superclass; cache_t cache;class_data_bits_t bits;这几个属性。

首先我们知道isa的指针是关联对象和类,superClass指向继承类,那么类的成员能够存储的地方就只有cache和bits

先看一下cache的结构体定义(不是一个结构体指针,是一个结构体),其中 mask_t为固定的4字节类型的值,而bucket_t则是一个8字节的指针,都不能存放我们定义的属性值,所以可以排除cache,这里也看出 cache的内存大小只有4+4+8=16字节

structcache_t {

    structbucket_t*_buckets;// 8

    mask_t_mask;              // 4

    mask_t_occupied;          // 4

所以可以猜测类的方法和属性在bits里面,继续看源码:

structobjc_class :objc_object{

    // Class ISA;

    Classsuper class;

    cache_t cache;            // formerly cache pointer and vtable

    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 

        return bits.data();

    }

我们可以看到class_rw_t 点击去:

structclass_rw_t {

  // Be warned that Symbolication knows the layout of this structure.

    uint32_t flags;

    uint32_t version;

    constclass_ro_t *ro;

    method_array_t  methods;

    property_array_t properties;

    protocol_array_t  protocols;

    Class firstSubclass;

    Class nextSiblingClass;

    char *demangledName;

#if SUPPORT_INDEXED_ISA

    uint32_t index;

#endif

由上我们是不是感觉类的属性和方法存在class_rw_t里面了,我们验证一下。

首先创建一个类LGPerson,添加成员变量hobby和属性nickName.添加示例方法和类方法。

@interfaceLGPerson :NSObject{

    NSString*hobby;

}

@property (nonatomic, copy) NSString *nickName;

- (void)sayHello;

+ (void)sayHappy;

@end

main函数中

 LGPerson*person = [LGPersonalloc];

  NSLog(@"%@ - %p",person,pClass);

xcode控制台:

x/4gx 打印person.class的内容,但是除了第一以及第二的内存,是我们熟悉的isa以及superClass指针以外,第三块地址的内容我们完全不知晓,第四块地址直接就不存在

(lldb) x/4gx person.class

0x100002338: 0x001d800100002311 0x0000000100afe140

0x100002348: 0x0000000101239a70 0x0000000200000003

按照Class结构体的成员定义顺序,以及内存对齐原则,我们尝试用指针偏移的方法,来找到第四块地址bits的所在,并且看看bits存放的内容是什么

(lldb) p/x 0x100002358

(long) $1 = 0x0000000100002358

这样我们什么也看不出来,看源码,我们用class_data_bits_t强转

(lldb) p (class_data_bits_t *)0x0000000100002358

(class_data_bits_t *) $2 = 0x0000000100002358

打印出class_data_bits_t的结构体。看上面源码我们之后rw是返回的是bits.data(),我们也调用一下,看看输出啥

(lldb) p $2->data()

(class_rw_t *) $3 = 0x000000010100cbe0

(lldb) p *$3

(class_rw_t) $4 = {

  flags = 2148139008

  version = 0

  ro = 0x0000000100002288

  methods = {

    list_array_tt = {

       = {

        list = 0x00000001000021d8

        arrayAndFlag = 4294975960

      }

    }

  }

  properties = {

    list_array_tt = {

       = {

        list = 0x0000000100002270

        arrayAndFlag = 4294976112

      }

    }

  }

  protocols = {

    list_array_tt = {

       = {

        list = 0x0000000000000000

        arrayAndFlag = 0

      }

    }

  }

  firstSubclass = nil

  nextSiblingClass = NSDate

  demangledName = 0x0000000000000000

}

通过上面输出我们可看到rw里面的properties里面并没有存储内容,那我们的成员变量和属性存在哪呢?在源码中我们直接被  method_array_t  methods; property_array_t properties;protocol_array_t  protocols;吸引,是否忘记了一个constclass_ro_t *ro;点击去:

structclass_ro_t {

    uint32_tflags;

    uint32_tinstanceStart;

    uint32_tinstanceSize;

#ifdef __LP64__

    uint32_treserved;

#endif

    constuint8_t* ivarLayout;


    constchar* name;

    method_list_t* baseMethodList;

    protocol_list_t* baseProtocols;

    constivar_list_t* ivars;

    constuint8_t* weakIvarLayout;

    property_list_t*baseProperties;

    method_list_t*baseMethods()const{

        return baseMethodList;

    }

};

我们发现这里面也有baseMethodList,baseProtocols,还有ivars,按照上面控制台来一遍;

 x/4gx person.class

0x100002338: 0x001d800100002311 0x0000000100afe140

0x100002348: 0x0000000101239a70 0x0000000200000003

(lldb) p/x 0x100002358

(long) $12 = 0x0000000100002358

(lldb) p (class_data_bits_t *)0x0000000100002358

(class_data_bits_t *) $13 = 0x0000000100002358

(lldb) p $13->data()

(class_rw_t *) $14 = 0x000000010100cbe0

(lldb) p $14->ro

(const class_ro_t *) $16 = 0x0000000100002288

(lldb) p *$16

(const class_ro_t) $17 = {

  flags = 388

  instanceStart = 8

  instanceSize = 24

  reserved = 0

  ivarLayout = 0x0000000100001f8d "\x02"

  name = 0x0000000100001f84 "LGPerson"

  baseMethodList = 0x00000001000021d8

  baseProtocols = 0x0000000000000000

  ivars = 0x0000000100002228

  weakIvarLayout = 0x0000000000000000

  baseProperties = 0x0000000100002270

}

(lldb) p $17.baseProperties

(property_list_t *const) $18 = 0x0000000100002270

(lldb) p *$18

(property_list_t) $19 = {

  entsize_list_tt = {

    entsizeAndFlags = 16

    count = 1

    first = (name = "nickName", attributes = "T@"NSString",C,N,V_nickName")

  }

}

我们想要的nickName出来了,那hobby呢?hobby肯定没在baseProperties里面,这个里面count=1,只存了一个值。hobby是成员变量,根据ro里面的字面意思肯定在ivars里面,我们打印看一下:

(lldb) p $17.ivars

(const ivar_list_t *const) $20 = 0x0000000100002228

(lldb) p *$20

(const ivar_list_t) $21 = {

  entsize_list_tt = {

    entsizeAndFlags = 32

    count = 2

    first = {

      offset = 0x0000000100002308

      name = 0x0000000100001e98 "hobby"

      type = 0x0000000100001faa "@"NSString""

      alignment_raw = 3

      size = 8

    }

  }

}

hobby出来了,眼睛毒辣的朋友会看到这里的count=2了,那是不是还有一个东西存在这呢?成员变量和属性是有区别的,在类中,我们定义的属性编译器也会自动的生成一个成员变量_nickName,所以我们在打印:

(lldb) p $21.get(1)

(ivar_t) $22 = {

  offset = 0x0000000100002300

  name = 0x0000000100001e9e "_nickName"

  type = 0x0000000100001faa "@"NSString""

  alignment_raw = 3

  size = 8

}

属性和成员变量都知道存在那了,那方法呢?字面意思baseMethodList,是不是在这里面,我们验证:

(lldb) p *$6

(const class_ro_t) $7 = {

  flags = 388

  instanceStart = 8

  instanceSize = 24

  reserved = 0

  ivarLayout = 0x0000000100001f8d "\x02"

  name = 0x0000000100001f84 "LGPerson"

  baseMethodList = 0x0000000100002238

  baseProtocols = 0x0000000000000000

  ivars = 0x00000001000022a0

  weakIvarLayout = 0x0000000000000000

  baseProperties = 0x00000001000022e8

}

(lldb) p $7.baseMethodList

(method_list_t *const) $8 = 0x0000000100002238

(lldb) p *$8

(method_list_t) $9 = {

  entsize_list_tt = {

    entsizeAndFlags = 26

    count = 4

    first = {

      name = "sayHello"

      types = 0x0000000100001f8f "v16@0:8"

      imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)

    }

  }

}

sayHello出来了,但是count=4这是为什么?不是只有2个方法吗?我们打印:

(lldb) p $9.get(0)

(method_t) $10 = {

  name = "sayHello"

  types = 0x0000000100001f8f "v16@0:8"

  imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)

}

(lldb) p $9.get(1)

(method_t) $11 = {

  name = "nickName"

  types = 0x0000000100001f97 "@16@0:8"

  imp = 0x0000000100001bf0 (LGTest`-[LGPerson nickName] at LGPerson.h:17)

}

(lldb) p $9.get(2)

(method_t) $12 = {

  name = "setNickName:"

  types = 0x0000000100001f9f "v24@0:8@16"

  imp = 0x0000000100001c20 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)

}

(lldb) p $9.get(3)

(method_t) $13 = {

  name = ".cxx_destruct"

  types = 0x0000000100001f8f "v16@0:8"

  imp = 0x0000000100001c60 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)

}

从以上结果看出,里面存了nickName的setter和getter方法,还有c++的析构函数destruct方法,怎么没有类方法+(void)sayHappy?我们都知道类方法是存在元类中的,我们去元类中找:

(lldb) x/4gx person

0x101840260: 0x001d8001000023b5 0x0000000000000000

0x101840270: 0x0000000000000000 0x0000000000000000

(lldb) p/x 0x001d8001000023b5 & 0x00007ffffffffff8

(long) $1 = 0x00000001000023b0

(lldb) x/4gx 0x00000001000023b0

0x1000023b0: 0x001d800100002389 0x0000000100afe140

0x1000023c0: 0x00000001003a0e80 0x0000000000000000

(lldb) p (class_data_bits_t *)0x1000023d0

(class_data_bits_t *) $2 = 0x00000001000023d0

(lldb) p $2->data()

(class_rw_t *) $3 = 0x00000001018408c0

(lldb) p $3.ro

(const class_ro_t *) $4 = 0x0000000100002300

  Fix-it applied, fixed expression was: 

    $3->ro

(lldb) p $3->ro

(const class_ro_t *) $5 = 0x0000000100002300

(lldb) p *$5

(const class_ro_t) $6 = {

  flags = 388

  instanceStart = 8

  instanceSize = 24

  reserved = 0

  ivarLayout = 0x0000000100001f8d "\x02"

  name = 0x0000000100001f84 "LGPerson"

  baseMethodList = 0x0000000100002238

  baseProtocols = 0x0000000000000000

  ivars = 0x00000001000022a0

  weakIvarLayout = 0x0000000000000000

  baseProperties = 0x00000001000022e8

}

(lldb) p $6.baseMethodList

(method_list_t *const) $7 = 0x0000000100002238

(lldb) p *$7

(method_list_t) $8 = {

  entsize_list_tt = {

    entsizeAndFlags = 26

    count = 4

    first = {

      name = "sayHello"

      types = 0x0000000100001f8f "v16@0:8"

      imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)

    }

  }

}

总结:

属性和成员变量存在ro的ivars里面,并且属性还存在ro的baseProperties中。

实例方法和类方法:我们自定义的实例方法和系统自动生成的setter、getter和c++的方法存在ro的baseMethodList,也就是类中。类方法存在元类中。

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