引言
我們都知道争舞,一個(gè)類有成員變量凛忿、對(duì)象方法、類方法,那么它們?cè)诘讓邮侨绾螌?shí)現(xiàn)的呢竞川?
前提
要想研究上面內(nèi)容店溢,首先要知道oc對(duì)象在c++
層面對(duì)應(yīng)的內(nèi)容叁熔,新創(chuàng)建一個(gè)工程,里面創(chuàng)建一個(gè)Dog類床牧,編譯main.m文件荣回,就會(huì)發(fā)現(xiàn)與main.m
同級(jí)的目錄下多了一個(gè)cpp
文件。
#import <Foundation/Foundation.h>
@interface Dog : NSObject
@property(nonatomic,copy)NSString*dogName;
-(void)eatFood;
-(void)playWithWhom:(NSString*)name;
@end
#import "Dog.h"
@implementation Dog
-(void)eatFood
{
NSLog(@"eatFood");
}
-(void)playWithWhom:(NSString*)name
{
NSLog(@"--%@",name);
}
@end
打開cpp
搜索關(guān)鍵字
dogName戈咳,可以看出Dog類是一個(gè)結(jié)構(gòu)體Dog_IMPL
驹马,我們繼續(xù)看NSObject_IMPL
,發(fā)現(xiàn)也是一個(gè)結(jié)構(gòu)體除秀,里面只有isa
指針活翩,并沒有我們定義的成員變量和方法挂脑。
NSObject
在runtime
中對(duì)應(yīng)的源碼為
類對(duì)象
的源碼為
接下來引出我們的重點(diǎn)
呐萌,我們可以看出objc_class
里最主要的三個(gè)成員變量骑篙。
Class superclass; //繼承 objc_object 的ISA 8字節(jié)
cache_t cache; // formerly cache pointer and vtable //16字節(jié)崩瓤,下面有詳解
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
其余的全是方法
不用考慮俊戳,因?yàn)榉椒ǖ拇鎯?chǔ)是在別的地方陆蟆。
首先寇窑,我們來研究class_data_bits_t bits
缎患,通過首地址
+指針偏移量
來獲取到bits
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //8個(gè)字節(jié)
union {
struct {
explicit_atomic<mask_t> _maybeMask; //4字節(jié)
#if __LP64__
uint16_t _flags; //2字節(jié)
#endif
uint16_t _occupied; //2字節(jié)
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache; //8個(gè)字節(jié)
};
...省略的是一些static變量(全局區(qū))和方法慕的,沒有影響。
}
由于聯(lián)合體union
共用內(nèi)存挤渔,所以union
為8
個(gè)字節(jié)肮街,所以cache_t
為 8 (uintptr_t)+ 8(union)=16字節(jié)。
結(jié)論:
想獲取bits
的值判导,就是首地址
偏移 8+8+16 = 32
個(gè)字節(jié)嫉父。
創(chuàng)建一個(gè)類:WJPerson
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface WJPerson : NSObject{
int age;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) float height;
- (void)playGuitar;
+ (void)eatFood;
// 1: 類的探究分析
// 2: 類方法 + ivar 在哪里
@end
NS_ASSUME_NONNULL_END
#import "WJPerson.h"
@implementation WJPerson
- (instancetype)init{
if (self = [super init]) {
self.name = @"wuji";
}
return self;
}
- (void)playGuitar{
}
+ (void)eatFood{
}
@end
那么,成員變量
和方法
到底存儲(chǔ)在哪里呢眼刃?
經(jīng)過源碼分析绕辖,我們發(fā)現(xiàn)class_data_bits_t
里面有個(gè)data
方法,返回的class_rw_t
是結(jié)構(gòu)體
類型擂红。
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
那么class_rw_t
里面有什么呢仪际?
struct class_rw_t {
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};
}
}
//存儲(chǔ)成員變量,比如age
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);
}
...//里面省略的包括一些copy之類的方法昵骤,這里不做研究
};
那么树碱,method_array_t
、property_array_t
变秦、protocol_array_t
里面會(huì)不會(huì)存儲(chǔ)著我們定義的方法
和成員變量
呢赴恨?
-
property_array_t:
(lldb) x/4gx WJPerson.class 0x100004470: 0x0000000100004498 0x0000000100354140 0x100004480: 0x000000010034b360 0x0000802000000000 (lldb) p/x 0x100004470+0x20 //首地址(第一個(gè)地址)+ 偏移量32字節(jié)(16進(jìn)制即0x20) (long) $5 = 0x0000000100004490 (lldb) p (class_data_bits_t*)0x0000000100004490 (class_data_bits_t *) $6 = 0x0000000100004490 (lldb) p $6->data() (class_rw_t *) $9 = 0x0000000100706010 //獲取class_rw_t結(jié)構(gòu)體 (lldb) p $9->properties() //獲取properties() (const property_array_t) $11 = { list_array_tt<property_t, property_list_t, RawPtr> = { = { list = { ptr = 0x0000000100004348 } arrayAndFlag = 4294984520 } } } (lldb) p $11.list (const RawPtr<property_list_t>) $12 = { ptr = 0x0000000100004348 } (lldb) p $12.ptr (property_list_t *const) $13 = 0x0000000100004348 (lldb) p *$13 (property_list_t) $14 = { entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2) } (lldb) p $14.get(0) (property_t) $15 = (name = "name", attributes = "T@\"NSString\",C,N,V_name") (lldb) p $14.get(1) (property_t) $16 = (name = "height", attributes = "Tf,N,V_height") (lldb) p $14.get(2) Assertion failed: (i < count), function get, file
至此,我們獲取到了屬性
name
和height
,但是我們并沒有看到成員變量age
伴栓,為什么伦连,age
到底在哪里雨饺,這里放到最后探究。 -
method_array_t:
(lldb) p $9->methods() (const method_array_t) $17 = { list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = { = { list = { ptr = 0x0000000100004248 } arrayAndFlag = 4294984264 } } } (lldb) p $17.list.ptr (method_list_t *const) $18 = 0x0000000100004248 (lldb) p *$18 (method_list_t) $19 = { entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6) } (lldb) p $19.get(0) (method_t) $20 = {}//這個(gè)地方直接get()打印不出來我們想要的結(jié)果了惑淳,下面分析 (lldb) p $19.get(0).big() (method_t::big) $21 = { name = "playGuitar" types = 0x0000000100003f72 "v16@0:8" imp = 0x0000000100003d20 (KCObjcBuild`-[WJPerson playGuitar]) } (lldb) p $19.get(1).big() (method_t::big) $22 = { name = "init" types = 0x0000000100003f6a "@16@0:8" imp = 0x0000000100003cc0 (KCObjcBuild`-[WJPerson init]) } (lldb) p $19.get(2).big() (method_t::big) $23 = { name = "name" types = 0x0000000100003f6a "@16@0:8" imp = 0x0000000100003d30 (KCObjcBuild`-[WJPerson name]) } (lldb) p $19.get(3).big() (method_t::big) $24 = { name = "height" types = 0x0000000100003f89 "f16@0:8" imp = 0x0000000100003d90 (KCObjcBuild`-[WJPerson height]) } (lldb) p $19.get(4).big() (method_t::big) $25 = { name = "setName:" types = 0x0000000100003f7a "v24@0:8@16" imp = 0x0000000100003d60 (KCObjcBuild`-[WJPerson setName:]) } (lldb) p $19.get(5).big() (method_t::big) $26 = { name = "setHeight:" types = 0x0000000100003f91 "v20@0:8f16" imp = 0x0000000100003db0 (KCObjcBuild`-[WJPerson setHeight:]) } (lldb) p $19.get(6).big() Assertion failed: (i < count), function get, file
至此额港,我們獲取到了- (void)playGuitar
,同時(shí)還有name
和height
的set歧焦、get
方法,但是我們并沒有看到+ (void)eatFood
移斩,為什么,+ (void)eatFood
到底在哪里绢馍,最后探究向瓷。
補(bǔ)充: method_t
和property_t
調(diào)用get()
分析
struct property_t {
const char *name;
const char *attributes; //這個(gè)里面有name和attributes可以輸出相關(guān)的信息
};
struct method_t { //這個(gè)里面什么都沒有,但是里面有一個(gè)big結(jié)構(gòu)體舰涌,它的里面有信息猖任。可以通過big()獲取
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
big &big() const {
ASSERT(!isSmall());
return *(struct big *)this;
}
-vars(成員變量)存儲(chǔ)
(lldb) p $9->ro()
(const class_ro_t *) $34 = 0x0000000100004200
(lldb) p *$34
(const class_ro_t) $35 = {
flags = 0
instanceStart = 8
instanceSize = 24
reserved = 0
= {
ivarLayout = 0x0000000000000000
nonMetaclass = nil
}
name = {
std::__1::atomic<const char *> = "WJPerson" {
Value = 0x0000000100003ee4 "WJPerson"
}
}
baseMethodList = 0x0000000100004248
baseProtocols = 0x0000000000000000
ivars = 0x00000001000042e0
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100004348
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $35.ivars
(const ivar_list_t *const) $36 = 0x00000001000042e0
(lldb) p *$36
(const ivar_list_t) $37 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $37.get(0)
(ivar_t) $38 = {
offset = 0x0000000100004408
name = 0x0000000100003e95 "age" //這里輸出了age成員變量
type = 0x0000000100003f85 "i"
alignment_raw = 2
size = 4
}
(lldb) p $37.get(1)
(ivar_t) $39 = {
offset = 0x0000000100004410
name = 0x0000000100003e99 "_height"
type = 0x0000000100003f87 "f"
alignment_raw = 2
size = 4
}
(lldb) p $37.get(2)
(ivar_t) $40 = {
offset = 0x0000000100004418
name = 0x0000000100003ea1 "_name"
type = 0x0000000100003f5e "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $37.get(3)
Assertion failed: (i < count), function get, file
至此瓷耙,我們從ivars獲取到了age
朱躺,同時(shí)還有_name
和_height
。
由上面也可得出一個(gè)結(jié)論:property
=ivar + get + set