objc-类和对象地址的确定

一.背景

https://github.com/iOSPrincekin/KCObjc4_debug.git dev分支

二.地址

1.类的地址

1.编写测试代码

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface LGPerson : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *hooby;

@property (nonatomic, weak) NSString *dress;

@property (nonatomic, weak) LGPerson *son;

@property (nonatomic, assign) int age;

@property (nonatomic, assign) double height;

@end

@implementation LGPerson

+(void)load{
    NSLog(@"load--");
}

-(void)test1{
    NSLog(@"test1--");
}
@end

LGPerson *globalPerson = nil;

void test1()
{
    NSObject* son = [[LGPerson alloc]init];
    [globalPerson setSon:son];
    NSLog(@"globalPerson.son-:%p",globalPerson.son);
}

@interface Sark : NSObject

@property (nonatomic, copy) NSString *name;

@end

@implementation Sark

- (void)speak {
    NSLog(@"my name's %@", self.name);
}

@end

void test2()
{
    Class obj_class = [NSObject class];
    Class Sark_class = [Sark class];
    
    id SarkMetaClass = objc_getMetaClass("Sark");

    BOOL res1 = [(id)obj_class isKindOfClass:obj_class];
    BOOL res2 = [(id)obj_class isMemberOfClass:obj_class];
    BOOL res3 = [(id)Sark_class isKindOfClass:Sark_class];
    BOOL res4 = [(id)Sark_class isMemberOfClass:Sark_class];
    
    BOOL res5 = [(id)Sark_class isMemberOfClass:[Sark_class class]];

    BOOL res6 = [(id)Sark_class isMemberOfClass:SarkMetaClass];

    id Sark_class_class = [Sark_class class];
    BOOL res7 = SarkMetaClass == Sark_class_class;
    
    BOOL res8 = [[[Sark alloc]init] isMemberOfClass:Sark_class];

    
    NSLog(@"%d %d %d %d %d %d %d %d", res1, res2, res3, res4, res5, res6, res7, res8);
}

@interface NSObject (Sark)
+ (void)foo;
@end
@implementation NSObject (Sark)
- (void)foo {
    NSLog(@"IMP: -[NSObject (Sark) foo]");
}
@end

void test3()
{
    // 测试代码
    [NSObject foo];
    [[NSObject new] foo];
}

void test4()
{
    id cls = [Sark class];
    void *obj = &cls;
    [(__bridge id)obj test1];
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        globalPerson = [LGPerson alloc];
        globalPerson.name = @"woshishui";
        globalPerson.hooby = @"chifan";
        globalPerson.dress = @"shuiyi";
        test1();
        globalPerson.age = 0x44;
        globalPerson.height = 0x77;
        [globalPerson test1];
        [globalPerson.son test1];
        NSLog(@"KCObjcBuild:%p",globalPerson);
        
        
        test2();
        test3();
      //  test4();
    }
    return 0;
}

2.ida分析可执行文件

请添加图片描述

可以看到 classlist 中包含 LGPerson 和 Sark

不需要懒加载的 类列表中即 nlclslist 总包含 LGPerson,因为 LGPerson 实现了 +load 方法,所以不需要懒加载,需要立即加载

分类类列表即 catlist 中包含 NSObject (Sark)

3.打印

在 readClass 方法中打印 LGPerson 类地址后输出


Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
    const char *mangledName = cls->nonlazyMangledName();
    printf("cls:%p,mangledName:%s\n",cls,mangledName);
    ......
   

4.印证地址

cls:0x1000085a0,mangledName:LGPerson

此时在 lldb debug 状态下,执行 image list



(lldb) image list
[  0] 2905B0F7-9E32-36D9-8717-18E1867196E4 0x0000000100000000 /Users/lee/Library/Developer/Xcode/DerivedData/objc-ascfiajdmnkqzabgrazgkmriraio/Build/Products/Debug/KCObjcBuild 



0x1000085a0 - 0x0000000100000000 = 0x85a0

在ida中点击 classlist 中的LGPerson,跳转到如下地址

![请添加图片描述](https://i-blog.csdnimg.cn/direct/89971314a6ca46dcbeef9a2c46558ec6.png

请添加图片描述

可以看到 0x85a0 是 LGPerson 类在 mach-o 的偏移量

可以看到 0x8578 是 LGPerson metaclass 在 mach-o 的偏移量

5.结论

类的地址是 运行后 mach-o 中对应的地址

2.对象的地址

1.先获取当前对象需要开辟空间的大小,这个是在编译完已经确定的,存储在 mach-o 中

请添加图片描述

断点截图 可见 ro->instanceSize = 56

请添加图片描述

和 mach-o 中 LGPerson的 objc2_class_ro 的 0x38 一致

2.调用堆栈


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
  * frame #0: 0x000000010049c0f9 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x0000000100008578, previously=0x0000000000000000) at objc-runtime-new.mm:2714:33
    frame #1: 0x000000010049bfa5 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x00000001000085a0, previously=0x0000000000000000) at objc-runtime-new.mm:2661:15
    frame #2: 0x000000010049e3da libobjc.A.dylib`_read_images(hList=0x00007ff7bfefb3d0, hCount=56, totalClasses=2148, unoptimizedTotalClasses=2148) at objc-runtime-new.mm:3848:13
    frame #3: 0x00000001004edb78 libobjc.A.dylib`map_images_nolock(mhCount=56, mhPaths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40, disabledClassROEnforcement=0x00007ff7bfefb6e7) at objc-os.mm:470:9
    frame #4: 0x000000010049d021 libobjc.A.dylib`map_images(count=56, paths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40) at objc-runtime-new.mm:3161:9
    frame #5: 0x00007ff801faa740 dyld`invocation function for block in dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*))::$_8::operator()() const + 637
    frame #6: 0x00007ff801fa26d9 dyld`dyld4::RuntimeState::withLoadersReadLock(void () block_pointer) + 41
    frame #7: 0x00007ff801fa75db dyld`dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*)) + 209
    frame #8: 0x00007ff801fcd188 dyld`dyld4::APIs::_dyld_objc_register_callbacks(_dyld_objc_callbacks const*) + 138
    frame #9: 0x00000001004eed3d libobjc.A.dylib`_objc_init at objc-os.mm:815:5
    frame #10: 0x00000001000adfb9 libdispatch.dylib`_os_object_init + 13
    frame #11: 0x00000001000bfd9c libdispatch.dylib`libdispatch_init + 363
    frame #12: 0x00007ff80e15f898 libSystem.B.dylib`libSystem_initializer + 238
    frame #13: 0x00007ff801fb43fb dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 175
    frame #14: 0x00007ff801ff2b7a dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 242
    frame #15: 0x00007ff801fe6f22 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 577
    frame #16: 0x00007ff801f970af dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 245
    frame #17: 0x00007ff801fe60bf dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 175
    frame #18: 0x00007ff801ff273a dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 470
    frame #19: 0x00007ff801fb166c dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 220
    frame #20: 0x00007ff801fd2bdd dyld`dyld4::APIs::runAllInitializersForMain() + 65
    frame #21: 0x00007ff801f9c874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #22: 0x00007ff801f9b3bd dyld`start + 1805

从堆栈中可以看出是在 realizeClassWithoutSwift 方法中设置 cls->setInstanceSize(ro->instanceSize);
会先递归调用realizeClassWithoutSwift设置父类,再是子类


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 13.1
  * frame #0: 0x00000001004cce66 libobjc.A.dylib`cache_t::setFastInstanceSize(this=0x00000001000085b0, newSize=56) at objc-runtime-new.h:595:18
    frame #1: 0x00000001004b0942 libobjc.A.dylib`objc_class::setInstanceSize(this=0x00000001000085a0, newSize=56) at objc-runtime-new.h:2651:15
    frame #2: 0x000000010049c01b libobjc.A.dylib`realizeClassWithoutSwift(cls=0x00000001000085a0, previously=0x0000000000000000) at objc-runtime-new.mm:2717:10
    frame #3: 0x000000010049e2da libobjc.A.dylib`_read_images(hList=0x00007ff7bfefb3d0, hCount=56, totalClasses=2148, unoptimizedTotalClasses=2148) at objc-runtime-new.mm:3848:13
    frame #4: 0x00000001004edb78 libobjc.A.dylib`map_images_nolock(mhCount=56, mhPaths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40, disabledClassROEnforcement=0x00007ff7bfefb6e7) at objc-os.mm:470:9
    frame #5: 0x000000010049cf21 libobjc.A.dylib`map_images(count=56, paths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40) at objc-runtime-new.mm:3161:9
    frame #6: 0x00007ff801faa740 dyld`invocation function for block in dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*))::$_8::operator()() const + 637
    frame #7: 0x00007ff801fa26d9 dyld`dyld4::RuntimeState::withLoadersReadLock(void () block_pointer) + 41
    frame #8: 0x00007ff801fa75db dyld`dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*)) + 209
    frame #9: 0x00007ff801fcd188 dyld`dyld4::APIs::_dyld_objc_register_callbacks(_dyld_objc_callbacks const*) + 138
    frame #10: 0x00000001004eed3d libobjc.A.dylib`_objc_init at objc-os.mm:815:5
    frame #11: 0x00000001000adfb9 libdispatch.dylib`_os_object_init + 13
    frame #12: 0x00000001000bfd9c libdispatch.dylib`libdispatch_init + 363
    frame #13: 0x00007ff80e15f898 libSystem.B.dylib`libSystem_initializer + 238
    frame #14: 0x00007ff801fb43fb dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 175
    frame #15: 0x00007ff801ff2b7a dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 242
    frame #16: 0x00007ff801fe6f22 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 577
    frame #17: 0x00007ff801f970af dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 245
    frame #18: 0x00007ff801fe60bf dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 175
    frame #19: 0x00007ff801ff273a dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 470
    frame #20: 0x00007ff801fb166c dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 220
    frame #21: 0x00007ff801fd2bdd dyld`dyld4::APIs::runAllInitializersForMain() + 65
    frame #22: 0x00007ff801f9c874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #23: 0x00007ff801f9b3bd dyld`start + 1805


 sizeBits = word_align(newSize) + FAST_CACHE_ALLOC_DELTA16;
 会对齐 16字节,将 56 + FAST_CACHE_ALLOC_DELTA16 = 64

3.对象初始化

请添加图片描述
可以看到调用 calloc 进行对象内存的开辟,开辟大小为刚才设置 cls->setInstanceSize(ro->instanceSize); 的64字节,用于存储对应的属性值和ISA,同时将 ISA 指向 LGPerson 类

调用堆栈



(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 19.1
    frame #0: 0x000000010048cdcd libobjc.A.dylib`objc_object::initIsa(this=0x000060000170c540, cls=LGPerson, nonpointer=true, hasCxxDtor=true) at objc-object.h:364:5
    frame #1: 0x00000001004b321e libobjc.A.dylib`objc_object::initInstanceIsa(this=0x000060000170c540, cls=LGPerson, hasCxxDtor=true) at objc-object.h:316:5
  * frame #2: 0x00000001004b3617 libobjc.A.dylib`_objc_rootAllocWithZone [inlined] _class_createInstanceFromZone(cls=LGPerson, extraBytes=0, zone=0x0000000000000000, construct_flags=2, cxxConstruct=true, outAllocatedSize=0x0000000000000000) at objc-runtime-new.mm:8448:14
    frame #3: 0x00000001004b349b libobjc.A.dylib`_objc_rootAllocWithZone(cls=LGPerson, zone=0x0000000000000000) at objc-runtime-new.mm:8475:12
    frame #4: 0x00000001004fc2b4 libobjc.A.dylib`_objc_rootAlloc [inlined] callAlloc(cls=LGPerson, checkNil=false, allocWithZone=true) at NSObject.mm:2004:16
    frame #5: 0x00000001004fc24c libobjc.A.dylib`_objc_rootAlloc(cls=LGPerson) at NSObject.mm:2020:12
    frame #6: 0x00000001005000a9 libobjc.A.dylib`+[NSObject alloc](self=LGPerson, _cmd="alloc") at NSObject.mm:2682:12
    frame #7: 0x00000001004fc3d4 libobjc.A.dylib`objc_alloc [inlined] callAlloc(cls=LGPerson, checkNil=true, allocWithZone=false) at NSObject.mm:2011:12
    frame #8: 0x00000001004fc32c libobjc.A.dylib`objc_alloc(cls=LGPerson) at NSObject.mm:2027:12
    frame #9: 0x0000000100003bcd KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:118:24 [opt]
    frame #10: 0x00007ff801f9b41f dyld`start + 1903


shiftcls = (uintptr_t)newCls >> 3;

什么是isa?

arm64架构之前,isa就是个普通的指针,存储类对象和元类对象的内存地址 arm64架构之后,isa变成了一个联合体结构(union),并且使用了位域来存储更多的信息



isa_t是一个联合体(联合体中的变量共用同一块地址空间。也就是说,cls、bits、结构体共用同一块地址空间)
union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    // 相当于是unsigned long bits;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
// 这里的定义在isa.h中,如下(注意uintptr_t实际上就是unsigned long)
//    uintptr_t nonpointer        : 1;
//    uintptr_t has_assoc         : 1;
//    uintptr_t has_cxx_dtor      : 1; 
//    uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
//    uintptr_t magic             : 6;
//    uintptr_t weakly_referenced : 1;
//    uintptr_t deallocating      : 1;
//    uintptr_t has_sidetable_rc  : 1;
//    uintptr_t extra_rc          : 8
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

struct objc_object {
    // isa结构体
private:
    isa_t isa;

public:
    // 类指针
    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
...
}




objc_class(类对象结构体)继承objc_object(实例对象结构体)



// objc_class继承于objc_object,因此
// objc_class中也有isa结构体
struct objc_class : objc_object {
    // ISA占8位
    Class ISA;
    // superclass占8位
    Class superclass;
    // 缓存的是指针和vtable,目的是加速方法的调用  cache占16位
    cache_t cache;             // formerly cache pointer and vtable
    // class_data_bits_t 相当于是class_rw_t 指针加上rr/alloc标志
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() {
        // 这里的bits就是class_data_bits_t bits;
        return bits.data();
    }
...
}




class_data_bits_t结构

class_data_bits_t用来存储数据,friend objc_class表明它有元类,uintptr_t bits通过掩码的形式保存class_rw_t是否是swift类等一些标志位
bool getBit(uintptr_t bit) const解析:

const 表示该方法内部不会修改 class_data_bits_t 的内部数据
返回值是一个 bool 类型,通过与操作来取出 bits 的指定位的值来进行判断

class_rw_t* data() const解析:

bits 中读出 class_rw_t 指针
void setData(class_rw_t *newData)解析:
设置class_rw_t 指针


struct class_data_bits_t {
    friend objc_class;
    // Values are the FAST_ flags above.
    uintptr_t bits;
private:
    bool getBit(uintptr_t bit) const
    {
    /* 
    内部实现只有一个与操作,主要根据入参 bit(掩码) 来取得一些标识位 
    如: 
    
    #define FAST_IS_SWIFT_LEGACY (1UL<<0) 
    使用 bit 的第 0 位来进行判断 
    
    #define FAST_IS_SWIFT_STABLE (1UL<<1)
    bit 的 第 1 位 判断类是否是稳定的 Swift ABI 的 Swift 类 
    */
        return bits & bit;
    }
public:
    class_rw_t* data() const {
        return (class_rw_t *)(bits & FAST_DATA_MASK);
    }
    void setData(class_rw_t *newData) {
        ...
    }
}




class_rw_t结构


struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    // 请注意,Symbolication 知道此结构的布局。
    
    // 在上篇的测试代码中:flags 打印看到是 2148007936
    // 转为二进制的话是只有 31 位和 19 位是 1,其它位全部都是 0,对应于:
    // class has started realizing but not yet completed it
    // #define RW_REALIZING (1<<19)
    // class_t->data is class_rw_t, not class_ro_t
    // #define RW_REALIZED (1<<31)
    
    uint32_t flags;
    
    //(控制台打印值为 1)
    uint16_t witness;
    
#if SUPPORT_INDEXED_ISA // isa 中保存 indexcls,大概是 watchOS 下才会用到
    uint16_t index;
#endif

    // std::atomic<uintptr_t>
    // 原子性 unsigned long
    
    // 执行如下命令,会打印 error:
    // (lldb) p $2->ro_or_rw_ext
    // error: no member named 'ro_or_rw_ext' in 'class_rw_t'
    
    // 在编译时会根据类定义生成类的 class_ro_t 数据,其中包含方法列表、属性列表、成员变量列表等等内容
    
    // ro_or_rw_ext 会有两种情况:
    // 1): 编译时值是 class_ro_t *
    // 2): 编译后类实现完成后值是 class_rw_ext_t *,而编译时的 class_ro_t * 作为 class_rw_ext_t 的 const class_ro_t *ro 成员变量保存
    explicit_atomic<uintptr_t> ro_or_rw_ext; // 变量名字对应与 class_ro_t or(或) class_rw_ext_t

    // 当前所属类的第一个子类
    // 测试时,定义了一个继承自 NSObject 的类,
    // 控制台打印看到它的 firstSubclass 是 nil
    Class firstSubclass;
    
    // 姊妹类、兄弟类
    // 测试时,定义了一个继承自 NSObject 的类,
    // 控制台打印看到 nextSiblingClass 是 NSUUID(好奇怪)
    
    // firstSubclass 和 nextSiblingClass 有超级重要的作用,后面会展开
    Class nextSiblingClass;
    ...
};



class_rw_t 的创建

在 realizeClassWithoutSwift 方法中


        // Normal class. Allocate writeable class data.
        rw = objc::zalloc<class_rw_t>();
        rw->set_ro(ro);
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
        cls->setData(rw);
        

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 11.1
  * frame #0: 0x00000001004b1934 libobjc.A.dylib`objc_class::setData(this=0x00007ff8459d0740, newData=0x000060000022bf40) at objc-runtime-new.h:2190:9
    frame #1: 0x000000010049bd69 libobjc.A.dylib`realizeClassWithoutSwift(cls=0x00007ff8459d0740, previously=0x0000000000000000) at objc-runtime-new.mm:2632:14
    frame #2: 0x000000010049e2da libobjc.A.dylib`_read_images(hList=0x00007ff7bfefb3d0, hCount=56, totalClasses=2148, unoptimizedTotalClasses=2148) at objc-runtime-new.mm:3848:13
    frame #3: 0x00000001004edb78 libobjc.A.dylib`map_images_nolock(mhCount=56, mhPaths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40, disabledClassROEnforcement=0x00007ff7bfefb6e7) at objc-os.mm:470:9
    frame #4: 0x000000010049cf21 libobjc.A.dylib`map_images(count=56, paths=0x00007ff7bfefc570, mhdrs=0x00007ff7bfefca40) at objc-runtime-new.mm:3161:9
    frame #5: 0x00007ff801faa740 dyld`invocation function for block in dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*))::$_8::operator()() const + 637
    frame #6: 0x00007ff801fa26d9 dyld`dyld4::RuntimeState::withLoadersReadLock(void () block_pointer) + 41
    frame #7: 0x00007ff801fa75db dyld`dyld4::RuntimeState::setObjCNotifiers(void (*)(unsigned int, char const* const*, mach_header const* const*), void (*)(char const*, mach_header const*), void (*)(char const*, mach_header const*), void (*)(mach_header const*, void*, mach_header const*, void const*), void (*)(unsigned int, _dyld_objc_notify_mapped_info const*)) + 209
    frame #8: 0x00007ff801fcd188 dyld`dyld4::APIs::_dyld_objc_register_callbacks(_dyld_objc_callbacks const*) + 138
    frame #9: 0x00000001004eed3d libobjc.A.dylib`_objc_init at objc-os.mm:815:5
    frame #10: 0x00000001000adfb9 libdispatch.dylib`_os_object_init + 13
    frame #11: 0x00000001000bfd9c libdispatch.dylib`libdispatch_init + 363
    frame #12: 0x00007ff80e15f898 libSystem.B.dylib`libSystem_initializer + 238
    frame #13: 0x00007ff801fb43fb dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 175
    frame #14: 0x00007ff801ff2b7a dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 242
    frame #15: 0x00007ff801fe6f22 dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 577
    frame #16: 0x00007ff801f970af dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 245
    frame #17: 0x00007ff801fe60bf dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 175
    frame #18: 0x00007ff801ff273a dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 470
    frame #19: 0x00007ff801fb166c dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 220
    frame #20: 0x00007ff801fd2bdd dyld`dyld4::APIs::runAllInitializersForMain() + 65
    frame #21: 0x00007ff801f9c874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #22: 0x00007ff801f9b3bd dyld`start + 1805







class_rw_ext_t结构


struct class_rw_ext_t {
    // 特别关注 ro 这个成员变量。
    // 这个即是在类实现完成后,class_rw_t 中存放的编译器生成的 class_ro_t 数据。
    const class_ro_t *ro;
    
    // 在上一节 class_ro_t 中的:
    // 方法列表、属性列表、成员变量列表、协议列表的类型如下:
    // struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3>
    // struct property_list_t : entsize_list_tt<property_t, property_list_t, 0>
    // struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0>
    // struct protocol_list_t 
    
    // 到 class_rw_t 中就变为了:
    // class method_array_t : public list_array_tt<method_t, method_list_t>
    // class property_array_t : public list_array_tt<property_t, property_list_t>
    // class protocol_array_t : public list_array_tt<protocol_ref_t, protocol_list_t>
    
    // 这里先不着急,等下会详细分析它们所使用的新的数据结构: list_array_tt。
    
    // 方法列表
    method_array_t methods;
    // 属性列表
    property_array_t properties;
    // 协议列表
    protocol_array_t protocols;
    
    // 所属的类名
    char *demangledName;
    // 版本号
    uint32_t version;
};





class_ro_t & class_rw_t & class_rw_ext_t & Class之间的关系

要了解它们之间的关系,我们首先要了解几个关键跟它们相关的概念。

名词解析

从字面意思解读:class_ro_t中的“ro”代表只读;class_rw_t中的“rw”代表可读可写;class_rw_ext_t中的“rw_ext”代表可读可写的扩展。
干净内存和脏内存:干净内存内存指的是内存一旦加载就不会被改变。反之,脏内存就是在运行时会被修改的。

关系释疑

Class本身是运行时加载的,在运行时会被改变,所以本身Class就是属于脏内存。那么如果想要获取Class的干净内存,也就是编译时确定的数据结构包括方法列表、成员变量等的,该怎么办?
这其实就是class_ro_t的作用。因为class_ro_t是只读,意味着 class_ro_t是从mach-o读取类的数据之后,就不会被改变。那如果我们想在运行时修改类的信息,比如添加方法,比如加载category怎么办呢?那这时候就有一个与之对应的class_rw_t结构,class_rw_t可以在运行时存储类的信息,可读可写的,可以在运行时修改。

说到这里,好像还漏掉一个结构class_rw_ext_t,这个东西又是干什么用的呢?存在的意义是什么?其实还是跟运行时有关。实际上在我们的app运行中,需要运行时修改的类是非常少的,据统计平均大概就10%左右。那也就是说大部分只需要读取class_ro_t中的数据就够了,少部分才需要修改。因此才会有class_rw_ext_t这个扩展的结构体。class_rw_ext_t的作用是这样的:当我们需要修改类结构时,比如添加方法、加载category等时,class_rw_t回去开辟一个额外的空间rwe(class_rw_ext_t),用于存储新的方法和class_ro_t中的方法等信息。这样做的目的有一个好处就是,对于绝大部分类是不需要这个开辟class_rw_ext_t这个结构体,节省内存。

创建时机

那这几个结构分别是什么时候创建的呢?这里就设计到类的加载流程。首先类在app启动的时候会被映射到内存,这时候会先创建Class(objc_class)结构,然后把类编译时的类数据映射到class_ro_t,class_ro_t结构体指针存储到Class的bits指针中,我们前面提到类的bits中存储的是class_rw_t指针,实际上在类初始化之前这里存储的是class_ro_t,等到类初始化的时候会创建一个class_rw_t结构,然后通过data()从bits中读取class_ro_t,然后class_rw_t通过set_ro(const class_ro_t *ro)把指针ro_or_rw_ext指向这个class_ro_t,然后Class通过setData()把class_rw_t指针存储到bits里面。然后在运行时根据需要,根据extAllocIfNeeded或extAlloc创建class_rw_ext_t,然后把class_ro_t关联到class_rw_ext_t,这就是大概流程。OC类的加载流程

3.分类是如何将方法添加到对应的类中的

1.找到分类对应的类

            Class cls = remapClass(cat->cls);

1.class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy)方法中创建 class_rw_ext_t 用于接收分类方法


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 10.1
  * frame #0: 0x000000010049a892 libobjc.A.dylib`class_rw_t::extAlloc(this=0x0000600000233040, ro=0x00007ff845244068, deepCopy=false) at objc-runtime-new.mm:1386:9
    frame #1: 0x00000001004a8f9c libobjc.A.dylib`class_rw_t::extAllocIfNeeded(this=0x0000600000233040) at objc-runtime-new.h:1834:20
    frame #2: 0x00000001004c419f libobjc.A.dylib`attachCategories(cls=0x00007ff8459e2c10, cats_list=0x00007ff7bfefd508, cats_count=1, flags=8) at objc-runtime-new.mm:1446:29
    frame #3: 0x00000001004c6485 libobjc.A.dylib`load_categories_nolock(header_info*)::$_10::operator()(this=0x00007ff7bfefd550, catlist=0x00007ffb43bc1d38) const at objc-runtime-new.mm:3235:25
    frame #4: 0x000000010049f123 libobjc.A.dylib`load_categories_nolock(hi=0x000060000260c600) at objc-runtime-new.mm:3254:5
    frame #5: 0x000000010049d0ba libobjc.A.dylib`loadAllCategories() at objc-runtime-new.mm:3262:9
    frame #6: 0x000000010049cfd6 libobjc.A.dylib`load_images(path="/usr/lib/system/libsystem_blocks.dylib", mh=0x00007ff80202e000) at objc-runtime-new.mm:3280:9
    frame #7: 0x00007ff801fa76d8 dyld`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 170
    frame #8: 0x00007ff801fd2c6f dyld`dyld4::APIs::runAllInitializersForMain() + 211
    frame #9: 0x00007ff801f9c874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #10: 0x00007ff801f9b3bd dyld`start + 1805

在 attachLists 分类之前 ro_or_rw_ext 指向 class_ro_t, attachLists 分类之后 ro_or_rw_ext 指向 class_rw_ext_t,并将 class_ro_t 设置给 class_rw_ext_t


    void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
        // the release barrier is so that the class_rw_ext_t::ro initialization
        // is visible to lockless readers
        rwe->ro = ro;
        ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
    }
    


    set_ro_or_rwe(rwe, ro);



2.先合并 class_ro_t 中内容


    property_list_t *proplist = ro->baseProperties;
    if (proplist) {
        rwe->properties.attachLists(&proplist, 1);
    }




(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.2
  * frame #0: 0x000000010049aceb libobjc.A.dylib`list_array_tt<property_t, property_list_t, RawPtr>::attachLists(this=0x0000600000c140d0, addedLists=0x00007ff7bfefcda0, addedCount=1) at objc-runtime-new.h:1653:24
    frame #1: 0x000000010049a8f7 libobjc.A.dylib`class_rw_t::extAlloc(this=0x0000600000233040, ro=0x00007ff845244068, deepCopy=false) at objc-runtime-new.mm:1398:25
    frame #2: 0x00000001004a8f9c libobjc.A.dylib`class_rw_t::extAllocIfNeeded(this=0x0000600000233040) at objc-runtime-new.h:1834:20
    frame #3: 0x00000001004c419f libobjc.A.dylib`attachCategories(cls=0x00007ff8459e2c10, cats_list=0x00007ff7bfefd508, cats_count=1, flags=8) at objc-runtime-new.mm:1446:29
    frame #4: 0x00000001004c6485 libobjc.A.dylib`load_categories_nolock(header_info*)::$_10::operator()(this=0x00007ff7bfefd550, catlist=0x00007ffb43bc1d38) const at objc-runtime-new.mm:3235:25
    frame #5: 0x000000010049f123 libobjc.A.dylib`load_categories_nolock(hi=0x000060000260c600) at objc-runtime-new.mm:3254:5
    frame #6: 0x000000010049d0ba libobjc.A.dylib`loadAllCategories() at objc-runtime-new.mm:3262:9
    frame #7: 0x000000010049cfd6 libobjc.A.dylib`load_images(path="/usr/lib/system/libsystem_blocks.dylib", mh=0x00007ff80202e000) at objc-runtime-new.mm:3280:9
    frame #8: 0x00007ff801fa76d8 dyld`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 170
    frame #9: 0x00007ff801fd2c6f dyld`dyld4::APIs::runAllInitializersForMain() + 211
    frame #10: 0x00007ff801f9c874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #11: 0x00007ff801f9b3bd dyld`start + 1805



3.再合并分类中的内容

		auto& entry = cats_list[i];
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);

        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);





(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
    frame #0: 0x000000010049aa5b libobjc.A.dylib`list_array_tt<method_t, method_list_t, method_list_t_authed_ptr>::attachLists(this=0x0000600000c140c8, addedLists=0x00007ff7bfefd4a8, addedCount=1) at objc-runtime-new.h:1653:24
  * frame #1: 0x00000001004c448b libobjc.A.dylib`attachCategories(cls=0x00007ff8459e2c10, cats_list=0x00007ff7bfefd508, cats_count=1, flags=8) at objc-runtime-new.mm:1485:22
    frame #2: 0x00000001004c6485 libobjc.A.dylib`load_categories_nolock(header_info*)::$_10::operator()(this=0x00007ff7bfefd550, catlist=0x00007ffb43bc1d38) const at objc-runtime-new.mm:3235:25
    frame #3: 0x000000010049f123 libobjc.A.dylib`load_categories_nolock(hi=0x000060000260c600) at objc-runtime-new.mm:3254:5
    frame #4: 0x000000010049d0ba libobjc.A.dylib`loadAllCategories() at objc-runtime-new.mm:3262:9
    frame #5: 0x000000010049cfd6 libobjc.A.dylib`load_images(path="/usr/lib/system/libsystem_blocks.dylib", mh=0x00007ff80202e000) at objc-runtime-new.mm:3280:9
    frame #6: 0x00007ff801fa76d8 dyld`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 170
    frame #7: 0x00007ff801fd2c6f dyld`dyld4::APIs::runAllInitializersForMain() + 211
    frame #8: 0x00007ff801f9c874 dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3761
    frame #9: 0x00007ff801f9b3bd dyld`start + 1805


4.对象如何找到对应方法

调用没有实现的 -[LGPerson test2]

1.首先会调用 objc_msgSend 方法


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
  * frame #0: 0x00000001004e6c86 libobjc.A.dylib`objc_msgSend at objc-msg-x86_64.s:732
    frame #1: 0x0000000100003bfc KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:119:22 [opt]
    frame #2: 0x00007ff801f9b41f dyld`start + 1903

通过 objc_msgSend 中的 GetIsaFast NORMAL // r10 = self->isa 获取到对象的 class

2.调用 lookUpImpOrForward 方法查找对应SEL


static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    lockdebug::assert_locked(&runtimeLock);

    ASSERT(cls->isRealized());
    // fixme nil cls?
    // fixme nil sel?

    auto const methods = cls->data()->methods();
    for (auto mlists = methods.beginLists(),
              end = methods.endLists();
         mlists != end;
         ++mlists)
    {
        // <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
        // caller of search_method_list, inlining it turns
        // getMethodNoSuper_nolock into a frame-less function and eliminates
        // any store from this codepath.
        method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }

    return nil;
}





(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 10.1
  * frame #0: 0x00000001004ad6b0 libobjc.A.dylib`getMethodNoSuper_nolock(cls=LGPerson, sel="setName:") at objc-runtime-new.mm:6527:13
    frame #1: 0x00000001004abe3a libobjc.A.dylib`lookUpImpOrForward(inst=0x000060000170c440, sel="setName:", cls=LGPerson, behavior=3) at objc-runtime-new.mm:6903:30
    frame #2: 0x00000001004e781b libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #3: 0x0000000100003bfc KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:119:22 [opt]
    frame #4: 0x00007ff801f9b41f dyld`start + 1903

循环到查找父类中有有没有方法,一直到父类不存在或找到为止为止



NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    ...
        const IMP forward_imp = (IMP)_objc_msgForward_impcache;
       IMP imp = nil;
       for (unsigned attempts = unreasonableClassCount();;) {
            if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
                // 找不到如何实现,resolveInstanceMethod方法实现也没找到,则 将 forward_imp 赋值给 imp ,调用 _objc_msgForward_impcache 方法转发 
                imp = forward_imp;
                break;
            }
                    
        // Superclass cache.
        imp = cache_getImp(curClass, sel);
        if (slowpath(imp == forward_imp)) {
            // Found a forward:: entry in a superclass.
            // Stop searching, but don't cache yet; call method
            // resolver for this class first.
            break;
        }
        ...
        }
     }

3.找不到方法的处理

1.找到方法,则跳转到结束

            // curClass method list.
            method_t *meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                imp = meth->imp(false);
                goto done;
            }
            
2.找不到方法,则进行转发

对象方法使用 resolveInstanceMethod 进行转发


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x00000001004cb263 libobjc.A.dylib`resolveInstanceMethod(inst=0x0000600001708180, sel="test2", cls=LGPerson) at objc-runtime-new.mm:6657:5
    frame #1: 0x00000001004ad74d libobjc.A.dylib`resolveMethod_locked(inst=0x0000600001708180, sel="test2", cls=LGPerson, behavior=1) at objc-runtime-new.mm:6708:9
    frame #2: 0x00000001004abee4 libobjc.A.dylib`lookUpImpOrForward(inst=0x0000600001708180, sel="test2", cls=LGPerson, behavior=1) at objc-runtime-new.mm:6943:16
    frame #3: 0x00000001004e77db libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #4: 0x0000000100003bd5 KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:124:9 [opt]
    frame #5: 0x00007ff8001ed41f dyld`start + 1903


查看 对象的 class 和 元类 class 有没有实现 对象方法 resolveInstanceMethod


static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    lockdebug::assert_unlocked(&runtimeLock);
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // Resolver not implemented.
        return;
    }

    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel);

    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
    

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
  * frame #0: 0x00000001004abe57 libobjc.A.dylib`lookUpImpOrForward(inst=0x0000600001708180, sel="test2", cls=LGPerson, behavior=4) at objc-runtime-new.mm:6923:54
    frame #1: 0x00000001004ac1ed libobjc.A.dylib`lookUpImpOrNilTryCache [inlined] _lookUpImpTryCache(inst=0x0000600001708180, sel="test2", cls=LGPerson, behavior=4) at objc-runtime-new.mm:6810:16
    frame #2: 0x00000001004ac168 libobjc.A.dylib`lookUpImpOrNilTryCache(inst=0x0000600001708180, sel="test2", cls=LGPerson, behavior=0) at objc-runtime-new.mm:6827:12
    frame #3: 0x00000001004cb323 libobjc.A.dylib`resolveInstanceMethod(inst=0x0000600001708180, sel="test2", cls=LGPerson) at objc-runtime-new.mm:6670:15
    frame #4: 0x00000001004ad74d libobjc.A.dylib`resolveMethod_locked(inst=0x0000600001708180, sel="test2", cls=LGPerson, behavior=1) at objc-runtime-new.mm:6708:9
    frame #5: 0x00000001004abee4 libobjc.A.dylib`lookUpImpOrForward(inst=0x0000600001708180, sel="test2", cls=LGPerson, behavior=1) at objc-runtime-new.mm:6943:16
    frame #6: 0x00000001004e77db libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #7: 0x0000000100003bd5 KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:124:9 [opt]
    frame #8: 0x00007ff8001ed41f dyld`start + 1903

如果没有实现 resolveInstanceMethod,则会调用 _objc_msgForward_impcache ,接着调用 _CF_forwarding_prep_0
寻找 forwardingTargetForSelector: 方法



(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1
    frame #0: 0x00000001004abe70 libobjc.A.dylib`lookUpImpOrForward(inst=0x0000000000000000, sel="forwardingTargetForSelector:", cls=LGSon, behavior=6) at objc-runtime-new.mm:6926:28
    frame #1: 0x00000001004ac1ed libobjc.A.dylib`lookUpImpOrNilTryCache [inlined] _lookUpImpTryCache(inst=0x0000000000000000, sel="forwardingTargetForSelector:", cls=LGSon, behavior=6) at objc-runtime-new.mm:6810:16
    frame #2: 0x00000001004ac168 libobjc.A.dylib`lookUpImpOrNilTryCache(inst=0x0000000000000000, sel="forwardingTargetForSelector:", cls=LGSon, behavior=2) at objc-runtime-new.mm:6827:12
    frame #3: 0x000000010048abea libobjc.A.dylib`class_respondsToSelector_inst(inst=0x0000000000000000, sel="forwardingTargetForSelector:", cls=LGSon) at objc-class.mm:662:26
    frame #4: 0x000000010048ab91 libobjc.A.dylib`class_respondsToSelector(cls=LGSon, sel="forwardingTargetForSelector:") at objc-class.mm:651:12
  * frame #5: 0x00007ff8006052da CoreFoundation`___forwarding___ + 179
    frame #6: 0x00007ff800605198 CoreFoundation`_CF_forwarding_prep_0 + 120
    frame #7: 0x0000000100003bd5 KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:138:9 [opt]
    frame #8: 0x00007ff8001ed41f dyld`start + 1903





如果类没有实现forwardingTargetForSelector方法,则会调用NSObject 的 forwardingTargetForSelector 方法 ,在 NSObject.mm 中 forwardingTargetForSelector 返回了空对象


+ (id)forwardingTargetForSelector:(SEL)sel {
    return nil;
}

- (id)forwardingTargetForSelector:(SEL)sel {
    return nil;
}


(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x00000001004ffc90 libobjc.A.dylib`-[NSObject forwardingTargetForSelector:](self=0x000060000171c000, _cmd="forwardingTargetForSelector:", sel="test2") at NSObject.mm:2586:5
    frame #1: 0x00007ff8006052f1 CoreFoundation`___forwarding___ + 202
    frame #2: 0x00007ff800605198 CoreFoundation`_CF_forwarding_prep_0 + 120
    frame #3: 0x0000000100003bd5 KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:138:9 [opt]
    frame #4: 0x00007ff8001ed41f dyld`start + 1903

接着会查找当前类是否实现 methodSignatureForSelector,如果没有找到则调用当前对象的 forwardInvocation方法包装后 进行转发


loc_116064:
    //**rbx就是实例对象,放到var_138暂存**
    var_138 = rbx;
    if (strncmp(r12, "_NSZombie_", 0xa) == 0x0) goto loc_1163cd;

loc_116087:
    //**r14 = 实例对象,r15 = 类对象**
    r14 = var_138;
    //**类对象是否响应methodSignatureForSelector方法**
    //**所以通过上面代码我们知道这是当forwardingTargetForSelector没匹配方法成功时调用**
    //**我们把这里成为慢速消息转发**
    if (class_respondsToSelector(r15, @selector(methodSignatureForSelector:)) == 0x0) goto loc_1163e3;

loc_1160a8:
    //**这里的rbx又等于SEL了**
    rbx = var_140;
    //**调用methodSignatureForSelector方法**
    rax = [r14 methodSignatureForSelector:rbx];
    //**返回值为0x0,则去loc_11645e位置**
    if (rax == 0x0) goto loc_11645e;
    
loc_1160c7:
    r15 = rax;
    rax = [rax _frameDescriptor];
    r12 = rax;
    if (((*(int16_t *)(*rax + 0x22) & 0xffff) >> 0x6 & 0x1) != r13) {
            rax = sel_getName(rbx);
            rcx = "";
            if ((*(int16_t *)(*r12 + 0x22) & 0xffff & 0x40) == 0x0) {
                    rcx = " not";
            }
            r8 = "";
            if (r13 == 0x0) {
                    r8 = " not";
            }
            _CFLog(0x4, @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'.  Signature thinks it does%s return a struct, and compiler thinks it does%s.", rax, rcx, r8, r9, var_150);
    }
    //**_forwardStackInvocation带下划线的基本都是系统内部函数**
    if (class_respondsToSelector(object_getClass(r14), @selector(_forwardStackInvocation:)) == 0x0) goto loc_1161fe;
    
loc_1161fe:
    var_150 = r12;
    r12 = r14;
    if (class_respondsToSelector(object_getClass(r14), @selector(forwardInvocation:)) == 0x0) goto loc_11642b;
    
loc_11622a:
    //**准备forwardInvocation方法需要的参数**
    rax = [NSInvocation _invocationWithMethodSignature:r15 frame:var_148];
    r13 = rax;
    //**调用forwardInvocation方法**
    [r12 forwardInvocation:rax];
    var_140 = 0x0;
    rbx = 0x0;
    r12 = var_150;
    goto loc_11626b;

loc_11642b:
    r14 = @selector(forwardInvocation:);
    ____forwarding___.cold.4(&var_130, r12);
    rcx = r14;
    _CFLog(0x4, @"*** NSForwarding: warning: object %p of class '%s' does not implement methodSignatureForSelector: -- trouble ahead", var_138, rcx, r8, r9, var_150);
    goto loc_116457;


如果在找不到,则会报错 doesNotRecognizeSelector 触发崩溃

loc_116457:
    rbx = var_140;
    goto loc_11645e;
    
loc_11645e:
    rax = sel_getName(rbx);
    r14 = rax;
    rax = sel_getUid(rax);
    if (rax != rbx) {
            rcx = r14;
            r8 = rax;
            _CFLog(0x4, @"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort", var_140, rcx, r8, r9, var_150);
    }
    if (class_respondsToSelector(object_getClass(var_138), @selector(doesNotRecognizeSelector:)) == 0x0) goto loc_1164d1;

loc_1164b8:
    (*_objc_msgSend)(var_138, @selector(doesNotRecognizeSelector:));
    asm { ud2 };
    rax = loc_1164d1(rdi, rsi, rdx, rcx, r8, r9);
    return rax;


5.initialize方法调用时机

    globalPerson = [LGPerson alloc]; 会调用  initialize方法

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00000001004abc30 libobjc.A.dylib`lookUpImpOrForward(inst=0x00000001000085a0, sel="initialize", cls=0x0000000100008578, behavior=3) at objc-runtime-new.mm:6836:33
    frame #1: 0x00000001004e781b libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #2: 0x00000001004923ec libobjc.A.dylib`_CALLING_SOME_+initialize_METHOD(cls=LGPerson) at objc-initialize.mm:384:5
    frame #3: 0x0000000100492931 libobjc.A.dylib`initializeNonMetaClass(cls=LGPerson) at objc-initialize.mm:549:13
    frame #4: 0x000000010049b59e libobjc.A.dylib`initializeAndMaybeRelock(cls=0x0000000100008578, inst=0x00000001000085a0, lock=0x000000010051b0c0, leaveLocked=true) at objc-runtime-new.mm:2216:5
    frame #5: 0x00000001004cb28a libobjc.A.dylib`initializeAndLeaveLocked(cls=0x0000000100008578, obj=0x00000001000085a0, lock=0x000000010051b0c0) at objc-runtime-new.mm:2232:12
    frame #6: 0x00000001004ac320 libobjc.A.dylib`realizeAndInitializeIfNeeded_locked(inst=0x00000001000085a0, cls=0x0000000100008578, initialize=true) at objc-runtime-new.mm:6765:15
    frame #7: 0x00000001004abdd9 libobjc.A.dylib`lookUpImpOrForward(inst=0x00000001000085a0, sel="alloc", cls=0x0000000100008578, behavior=11) at objc-runtime-new.mm:6882:11
    frame #8: 0x00000001004e781b libobjc.A.dylib`_objc_msgSend_uncached at objc-msg-x86_64.s:1153
    frame #9: 0x00000001004fc3d4 libobjc.A.dylib`objc_alloc [inlined] callAlloc(cls=LGPerson, checkNil=true, allocWithZone=false) at NSObject.mm:2011:12
    frame #10: 0x00000001004fc32c libobjc.A.dylib`objc_alloc(cls=LGPerson) at NSObject.mm:2027:12
  * frame #11: 0x0000000100003bcd KCObjcBuild`main(argc=1, argv=0x00007ff7bfeff450) at main.m:118:24 [opt]
    frame #12: 0x00007ff801f9b41f dyld`start + 1903
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值