当前位置: 首页 > 工具软件 > BITS > 使用案例 >

重学OC第四篇:类结构之bits

柳刚豪
2023-12-01

前言

在上一篇中已经知道类的结构体为

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标记。
那么方法、属性、成员变量等信息存在了哪里?

一、bits有什么?方法?属性?成员变量?

1 class_data_bits_t

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指针。

2 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 *>()->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。

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

2.2 class_ro_t

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方法。

因时间原因,协议和属性有时间再验证。

总结

  1. 类方法存在于类的元类中,实例方法、属性存在类中
  2. 编译时的方法等存在class_ro_t中,运行时的方法存在class_rw_ext_t中,class_rw_ext_t中也包含了class_ro_t中的方法,class_rw_t封装了methods、properties()、protocols方法来方便调用。
  3. 类的变量存在class_ro_t中的ivars中。

有点乱,感觉还是要把类的加载入实例化过程等知识了解更深点,然后才能串起来这条线。

//TODO: 具体理解ro与rwe间的转换

 类似资料: