在上一篇中已经知道类的结构体为
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
......
}
第一个存放的isa,第二个存放的父类,第三个从字面理解是缓存,第四个从上面的注释可以得知,bits里主要的信息就是class_rw_t *加上一些自定义rr/alloc标记。
那么方法、属性、成员变量等信息存在了哪里?
struct class_data_bits_t {
friend objc_class;
// Values are the FAST_ flags above.
uintptr_t bits;
......
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
ASSERT(!data() || (newData->flags & (RW_REALIZING | RW_FUTURE)));
// Set during realization or construction only. No locking needed.
// Use a store-release fence because there may be concurrent
// readers of data and data's contents.
uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData;
atomic_thread_fence(memory_order_release);
bits = newBits;
}
// Get the class's ro data, even in the presence of concurrent realization.
// fixme this isn't really safe without a compiler barrier at least
// and probably a memory barrier when realizeClass changes the data field
const class_ro_t *safe_ro() {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
.......
}
通过friend关键字把objc_class设为了友元结构体,从而可以访问其私有内容。
class_data_bits_t中只有一个bits变量,从注释中得知里面存着以FAST_开头的定义的flags,例如FAST_HAS_DEFAULT_RR、FAST_CACHE_HAS_CXX_DTOR等等。class_data_bits_t结构体里并没有看到方法、属性相关的内容,但是通过data()方法可以看到bits变量中还存有一个class_rw_t类型的data指针。
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 *>()->methods;
} else {
return method_array_t{v.get<const class_ro_t *>()->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 *>()->properties;
} else {
return property_array_t{v.get<const class_ro_t *>()->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 *>()->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>()->baseProtocols};
}
}
};
在class_rw_t结构体中我们找到了methods()、properties()、protocols()方法,从methods()方法体中看到它有两种返回情况,一种是class_rw_ext_t,一种是class_ro_t。
struct class_rw_ext_t {
const class_ro_t *ro; //存放的就是ro的内容
method_array_t methods; //存放ro中的方法+动态添加的方法
property_array_t properties;
protocol_array_t protocols;
char *demangledName;
uint32_t version; //元类7,非元类0
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
// This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
_objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];
_objc_swiftMetadataInitializer swiftMetadataInitializer() const {
if (flags & RO_HAS_SWIFT_INITIALIZER) {
return _swiftMetadataInitializer_NEVER_USE[0];
} else {
return nil;
}
}
method_list_t *baseMethods() const {
return baseMethodList;
}
class_ro_t *duplicate() const {
if (flags & RO_HAS_SWIFT_INITIALIZER) {
size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
return ro;
} else {
size_t size = sizeof(*this);
class_ro_t *ro = (class_ro_t *)memdup(this, size);
return ro;
}
}
};
可以在这里面看到有baseMethodList、ivars、baseProperties等信息,那么方法、成员变量、属性到底存没存在这些地方?如果是存在这些地方,class_rw_ext_t和class_ro_t中存的方法、属性、协议信息又有什么区别?我们通过代码去验证一下。
@interface TestA : NSObject
@property(nonatomic, copy) NSString *nickname;
- (void)sayHello;
+ (void)sayAny;
@end
@implementation TestA
- (void)sayHello {
}
+ (void)sayAny {
}
@end
@interface TestA(SayU)
- (void)sayU;
@end
@implementation TestA(SayU)
- (void)sayU {
}
@end
void sayMe() {
printf("say me");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
TestA *a = [[TestA alloc] init];
// IMP imp = (IMP)sayMe;
// class_addMethod(TestA.class, @selector(sayMe), imp, "v@:");
// [a performSelector:@selector(sayMe)];
}
return 0;
}
在main中打上断点
(lldb) p/x TestA.class
(Class) $0 = 0x0000000100002270 TestA
(lldb) p sizeof(cache_t) //得到cache_t的数据类型大小
(unsigned long) $1 = 16
(lldb) p/x 0x0000000100002270 + 0x20
(long) $2 = 0x0000000100002290
(lldb) p (class_data_bits_t *)0x0000000100002290
(class_data_bits_t *) $3 = 0x0000000100002290
(lldb) p $3->data()
(class_rw_t *) $4 = 0x000000010105d770
(lldb) p $4->methods()
(const method_array_t) $5 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100002038
arrayAndFlag = 4294975544
}
}
}
(lldb) p $5.list
(method_list_t *const) $6 = 0x0000000100002038
(lldb) p *$6
(method_list_t) $7 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 5
first = {
name = "sayHello"
types = 0x0000000100000f50 "v16@0:8"
imp = 0x0000000100000d60 (KCObjc`-[TestA sayHello] at main.m:22)
}
}
}
(lldb) p $7.get(0)
(method_t) $8 = {
name = "sayHello"
types = 0x0000000100000f50 "v16@0:8"
imp = 0x0000000100000d60 (KCObjc`-[TestA sayHello] at main.m:22)
}
(lldb) p $7.get(1)
(method_t) $9 = {
name = "sayU"
types = 0x0000000100000f50 "v16@0:8"
imp = 0x0000000100000e00 (KCObjc`-[TestA(SayU) sayU] at main.m:40)
}
(lldb) p $7.get(2)
(method_t) $10 = {
name = ".cxx_destruct"
types = 0x0000000100000f50 "v16@0:8"
imp = 0x0000000100000dd0 (KCObjc`-[TestA .cxx_destruct] at main.m:20)
}
(lldb) p $7.get(3)
(method_t) $11 = {
name = "nickname"
types = 0x0000000100000f64 "@16@0:8"
imp = 0x0000000100000d70 (KCObjc`-[TestA nickname] at main.m:13)
}
(lldb) p $7.get(4)
(method_t) $12 = {
name = "setNickname:"
types = 0x0000000100000f6c "v24@0:8@16"
imp = 0x0000000100000da0 (KCObjc`-[TestA setNickname:] at main.m:13)
}
从上面可以看到除了类方法+sayAny, 其他的方法都存在methods()中了。那么sayAny方法在哪呢?
(lldb) x/4gx TestA.class
0x100002270: 0x0000000100002248 0x0000000100333140
0x100002280: 0x000000010032d440 0x0000801c00000000
(lldb) p/x 0x0000000100002248 & 0x00007ffffffffff8ULL //拿到元类
(unsigned long long) $1 = 0x0000000100002248
(lldb) po $1
TestA
(lldb) p ((class_data_bits_t *)(0x0000000100002248 + 0x20))->data()->methods().list
(method_list_t *const) $2 = 0x0000000100002100
(lldb) p *$2
(method_list_t) $3 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayAny"
types = 0x0000000100000f57 "v16@0:8"
imp = 0x0000000100000d50 (KCObjc`+[TestA sayAny] at main.m:26)
}
}
}
所以类方法是存在元类中的。
(lldb) p/x TestA.class
(Class) $0 = 0x0000000100002270 TestA
(lldb) p ((class_data_bits_t *)(0x0000000100002270 + 0x20))->data()->ro()
(const class_ro_t *) $1 = 0x0000000100002120
(lldb) p *$1
(const class_ro_t) $2 = {
flags = 388
instanceStart = 8
instanceSize = 16
reserved = 0
ivarLayout = 0x0000000100000f04 "\x01"
name = 0x0000000100000efe "TestA"
baseMethodList = 0x0000000100002038
baseProtocols = 0x0000000000000000
ivars = 0x0000000100002168
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100002190
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p *$2.ivars
(const ivar_list_t) $3 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
count = 1
first = {
offset = 0x0000000100002240
name = 0x0000000100000f1b "_nickname"
type = 0x0000000100000f5f "@\"NSString\""
alignment_raw = 3
size = 8
}
}
}
(lldb) p *$2.baseMethodList
(method_list_t) $4 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 5
first = {
name = "sayHello"
types = 0x0000000100000f57 "v16@0:8"
imp = 0x0000000100000d60 (KCObjc`-[TestA sayHello] at main.m:22)
}
}
}
类的成员变量存在于class_ro_t的ivars中, 这里可以看到class_ro_t的baseMethodList同样也存有实例方法,那跟class_rw_ext_t中的方法有什么区别呢?打开main函数中注释的代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here... 0x00007ffffffffff8ULL
TestA *a = [[TestA alloc] init];
IMP imp = (IMP)sayMe;
class_addMethod(TestA.class, @selector(sayMe), imp, "v@:");
[a performSelector:@selector(sayMe)];
}
return 0;
}
在[a performSelector:@selector(sayMe)]处打上断点,我们按前面的调试再走一遍
(lldb) p/x TestA.class
(Class) $0 = 0x0000000100002290 TestA
(lldb) p/x 0x0000000100002290 + 0x20
(long) $1 = 0x00000001000022b0
(lldb) p/x (class_data_bits_t *)0x00000001000022b0
(class_data_bits_t *) $2 = 0x00000001000022b0
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001012bfcd0
(lldb) p $3->methods()
(const method_array_t) $4 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x00000001010d0f31
arrayAndFlag = 4312600369
}
}
}
(lldb) p $4.list
(method_list_t *const) $5 = 0x00000001010d0f31
(lldb) p *$5
(method_list_t) $6 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 0
count = 268435456
first = {
name = <no value available>
types = 0x0000000001000020 ""
imp = 0x0000000000000000
}
}
}
(lldb) p $6.get(0)
(method_t) $7 = {
name = <no value available>
types = 0x0000000001000020 ""
imp = 0x0000000000000000
}
(lldb) p $6.get(1)
(method_t) $8 = {
name = <no value available>
types = 0x0000000001000020 ""
imp = 0x0000000000000000
}
可以看到最后$6, $7, $8完全不是我们想要的数据,发生了什么?我就通过class_addMethod动态添加了一个方法,那只好跟踪下class_addMethod方法都干啥了
static IMP
addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
IMP result = nil;
runtimeLock.assertLocked();
checkIsKnownClass(cls);
ASSERT(types);
ASSERT(cls->isRealized());
method_t *m;
if ((m = getMethodNoSuper_nolock(cls, name))) {
// already exists
if (!replace) {
result = m->imp;
} else {
result = _method_setImplementation(cls, m, imp);
}
} else {
auto rwe = cls->data()->extAllocIfNeeded();
// fixme optimize
method_list_t *newlist;
newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
newlist->entsizeAndFlags =
(uint32_t)sizeof(method_t) | fixed_up_method_list;
newlist->count = 1;
newlist->first.name = name;
newlist->first.types = strdupIfMutable(types);
newlist->first.imp = imp;
prepareMethodLists(cls, &newlist, 1, NO, NO);
rwe->methods.attachLists(&newlist, 1);
flushCaches(cls);
result = nil;
}
return result;
}
通过class_addMethod方法进入addMethod,然后进入了else中,可以看到在这里创建了rwe,这里有个extAllocIfNeeded方法很重要, 它会调用extAlloc方法
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>();
} else {
return extAlloc(v.get<const class_ro_t *>());
}
}
class_rw_ext_t *
class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)
{
runtimeLock.assertLocked();
auto rwe = objc::zalloc<class_rw_ext_t>();
rwe->version = (ro->flags & RO_META) ? 7 : 0;
method_list_t *list = ro->baseMethods();
if (list) {
if (deepCopy) list = list->duplicate();
rwe->methods.attachLists(&list, 1);
}
// See comments in objc_duplicateClass
// property lists and protocol lists historically
// have not been deep-copied
//
// This is probably wrong and ought to be fixed some day
property_list_t *proplist = ro->baseProperties;
if (proplist) {
rwe->properties.attachLists(&proplist, 1);
}
protocol_list_t *protolist = ro->baseProtocols;
if (protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
set_ro_or_rwe(rwe, ro);
return rwe;
}
我们现在主要看的是方法,所以主要看method那块代码,然后rwe->methods.attachLists(&list, 1)
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
array()->count = newCount;
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
}
else {
// 1 list -> many lists
List* oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists,
addedCount * sizeof(array()->lists[0]));
}
}
因为我们只添加了一个方法,所以这里会走到else,注意看这两行代码
if (oldList) array()->lists[addedCount] = oldList;
memcpy(array()->lists, addedLists, addedCount * sizeof(array()->lists[0]));
struct array_t {
uint32_t count;
List* lists[0];
static size_t byteSize(uint32_t count) {
return sizeof(array_t) + count*sizeof(lists[0]);
}
size_t byteSize() {
return byteSize(count);
}
};
array_t *array() const {
return (array_t *)(arrayAndFlag & ~1);
}
array()返回的是个array_t *, 而array()又指向了lists数组,这里相当于是个指向指针的指针形式。
现在去到class_rw_t中,在methods()方法里打上断点
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 *>()->methods;
} else {
return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
}
}
template <typename T> T get() const {
ASSERT(is<T>() && "Invalid accessor called");
return reinterpret_cast<T>(getPointer());
}
回到main中去让[a performSelector:@selector(sayMe)]执行,然后会断到methods()方法中,这一步会走进来是因为performSelector相当于调用了objc_msgSend, 会查找sayMe方法,而方法都存在methods中, 然后会看到走进了if判断中
(lldb) p v
(objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>) $0 = (_value = 4302144033)
(lldb) exp v.getPointer() //v.get<const class_ro_t *>()用p不出来,所以算出getPointer()的值
(uintptr_t) $1 = 4302144032
(lldb) p reinterpret_cast<class_rw_ext_t *>(4302144032) //到这就相当于通过v.get<const class_ro_t *>()拿到了class_rw_ext_t *的数据
(class_rw_ext_t *) $2 = 0x00000001006d8220
(lldb) p $2->methods
(method_array_t) $3 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x00000001006d8271
arrayAndFlag = 4302144113
}
}
}
(lldb) p $3.list
(method_list_t *) $4 = 0x00000001006d8271
(lldb) p *$4
(method_list_t) $5 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 0
count = 1342177280
first = {
name = <no value available>
types = 0x9800000001000020 ""
imp = 0x0000007fff8e0c1a (0x0000007fff8e0c1a)
}
}
}
可以看到$5的数据还是不对,彻底懵逼,经过对method_array_t类分析,看到method_array_t继承的是list_array_tt类,这个类刚好就是前面attachLists中array()所在的类,想到前面rwe->methods进行attachLists时,操作的也是这个类中的array_t结构体中的lists, 又看到以下方法
iterator begin() const {
return iterator(beginLists(), endLists());
}
List* const * beginLists() const {
if (hasArray()) {
return array()->lists;
} else {
return &list;
}
}
然后就可以直接取出lists中的数据来看
(lldb) p $4.begin()
(list_array_tt<method_t, method_list_t>::iterator) $7 = {
lists = 0x0000000100a66c98
listsEnd = 0x0000000100a66ca8
m = {
entsize = 24
index = 0
element = 0x0000000100a66c78
}
mEnd = {
entsize = 24
index = 1
element = 0x0000000100a66c90
}
}
(lldb) p $4.beginLists()
(method_list_t *const *) $8 = 0x0000000100a66c98 //这个是通过class_addMethod运态添加进来的方法存放的lists
(lldb) p **$8
(method_list_t) $9 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayMe"
types = 0x0000000100000fa9 "v@:"
imp = 0x0000000100000d90 (KCObjc`sayMe at main.m:61)
}
}
}
(lldb) p/x 0x0000000100a66c98 + 0x08 //取$8的地址然后偏移sizeof(method_list_t *const *)位到下一个存放ro中方法的lists的指针
(long) $13 = 0x0000000100a66ca0
(lldb) p (method_list_t *const *)0x0000000100a66ca0
(method_list_t *const *) $17 = 0x0000000100a66ca0
(lldb) p **$17
(method_list_t) $18 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 5
first = {
name = "sayHello"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000ce0 (KCObjc`-[TestA sayHello] at main.m:22)
}
}
}
(lldb) p $18.get(0)
(method_t) $19 = {
name = "sayHello"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000ce0 (KCObjc`-[TestA sayHello] at main.m:22)
}
(lldb) p $18.get(1)
(method_t) $20 = {
name = "sayU"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000d80 (KCObjc`-[TestA(SayU) sayU] at main.m:54)
}
(lldb) p $18.get(2)
(method_t) $21 = {
name = ".cxx_destruct"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000d50 (KCObjc`-[TestA .cxx_destruct] at main.m:20)
}
(lldb) p $18.get(3)
(method_t) $22 = {
name = "nickname"
types = 0x0000000100000f6a "@16@0:8"
imp = 0x0000000100000cf0 (KCObjc`-[TestA nickname] at main.m:13)
}
(lldb) p $18.get(4)
(method_t) $23 = {
name = "setNickname:"
types = 0x0000000100000f72 "v24@0:8@16"
imp = 0x0000000100000d20 (KCObjc`-[TestA setNickname:] at main.m:13)
}
我们再来看看此时的ro中的方法
(lldb) p $3.ro //$3是前面的class_rw_ext_t,在是这个类型时,ro就是直接从这个rwe中取的
(const class_ro_t *) $24 = 0x00000001008ba360
(lldb) p *$24
(const class_ro_t) $25 = {
flags = 388
instanceStart = 8
instanceSize = 16
reserved = 0
ivarLayout = 0x0000000100000eec "\x01"
name = 0x0000000100000ee6 "TestA"
baseMethodList = 0x0000000100002048
baseProtocols = 0x0000000000000000
ivars = 0x0000000100002178
weakIvarLayout = 0x0000000000000000
baseProperties = 0x00000001000021a0
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $25.baseMethodList
(method_list_t *const) $26 = 0x0000000100002048
(lldb) p *$26
(method_list_t) $27 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 5
first = {
name = "sayHello"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000ce0 (KCObjc`-[TestA sayHello] at main.m:22)
}
}
}
(lldb) p $27.get(0)
(method_t) $28 = {
name = "sayHello"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000ce0 (KCObjc`-[TestA sayHello] at main.m:22)
}
(lldb) p $27.get(1)
(method_t) $29 = {
name = "sayU"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000d80 (KCObjc`-[TestA(SayU) sayU] at main.m:54)
}
(lldb) p $27.get(2)
(method_t) $30 = {
name = ".cxx_destruct"
types = 0x0000000100000f56 "v16@0:8"
imp = 0x0000000100000d50 (KCObjc`-[TestA .cxx_destruct] at main.m:20)
}
(lldb) p $27.get(3)
(method_t) $31 = {
name = "nickname"
types = 0x0000000100000f6a "@16@0:8"
imp = 0x0000000100000cf0 (KCObjc`-[TestA nickname] at main.m:13)
}
(lldb) p $27.get(4)
(method_t) $32 = {
name = "setNickname:"
types = 0x0000000100000f72 "v24@0:8@16"
imp = 0x0000000100000d20 (KCObjc`-[TestA setNickname:] at main.m:13)
}
通过对比可以知道,class_rw_ext_t中的methods中除了存放了ro的baseMethods中的方法,还存有通过runtime的class_addMethod添加进来的sayMe方法。
因时间原因,协议和属性有时间再验证。
有点乱,感觉还是要把类的加载入实例化过程等知识了解更深点,然后才能串起来这条线。
//TODO: 具体理解ro与rwe间的转换