iOS底层探索 -- objc与dyld关联分析

引子

在我们之前探索dyld流程时,我们发现其实dyldobjc_init()之间是存在联系的。

iOS底层探索 -- dyld 流程分析

其中在我们追踪到_dyld_objc_notify_register 方法时,我们能发现其调用的入口是 _objc_init(), 那么顺着这个线索,我们来看一看这个_objc_init()方法。

objc_init()

首先,我们继续使用我们之前的objc源码工程

我们来看一下objc_init()源码

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    //读取环境变量
    environ_init();
    //关于线程key的绑定,例如线程数据的析构函数
    tls_init();
    //运行C++静态构造函数。
    //在dyld调用静态构造函数之前,libc调用了objc_init(),
    //所以我们必须自己动手。
    static_init();
    //runtime运行时环境初始化
    runtime_init();
    //初始化libobjc的异常处理系统。
    //由map_images()调用。
    exception_init();
    //缓存条件的初始化
    cache_init();
    //初始化回调机制。通常情况下,这里不会做什么
    //所有的初始化都是惰性的,但是对于某些进程,我们急切地加载
    _imp_implementationWithBlock_init();
    //注册要在映射、取消映射和初始化objc映像时调用的处理程序。
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

其中我们能看到中间进行了很多初始化的工作

  • environ_init() 读取环境变量
  • tls_init() 关于线程key的绑定,例如线程数据的析构函数
  • static_init() 运行C++静态构造函数。在dyld调用静态构造函数之前,libc调用了objc_init()所以我们必须自己动手。
  • exception_init() 初始化libobjc的异常处理系统。由map_images()调用。
  • cache_init() 缓存条件的初始化。
  • _imp_implementationWithBlock_init() 初始化回调机制。通常情况下,这里不会做什么。所有的初始化都是惰性的,但是对于某些进程,我们急切地加载
  • _dyld_objc_notify_register() 注册要在映射、取消映射和初始化objc映像时调用的处理程序。

environ_init()

void environ_init(void) 
{
    ....
    // Print OBJC_HELP and OBJC_PRINT_OPTIONS output.
    if (PrintHelp  ||  PrintOptions) {
        if (PrintHelp) {
            _objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
            _objc_inform("OBJC_HELP: describe available environment variables");
            if (PrintOptions) {
                _objc_inform("OBJC_HELP is set");
            }
            _objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
        }
        if (PrintOptions) {
            _objc_inform("OBJC_PRINT_OPTIONS is set");
        }
        for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
            const option_t *opt = &Settings[i];            
            if (PrintHelp) _objc_inform("%s: %s", opt->env, opt->help);
            if (PrintOptions && *opt->var) _objc_inform("%s is set", opt->env);
        }
    }
}

我们发现其中的for()循环可以在满足条件下打印环境标量

我们想了解到底有哪些环境变量,那我们直接拿出其中的for()循环,并去掉所有条件

for (size_t i = 0; i < sizeof(Settings)/sizeof(Settings[0]); i++) {
    const option_t *opt = &Settings[I];
     _objc_inform("%s: %s", opt->env, opt->help);
     _objc_inform("%s is set", opt->env);
}

直接运行,得到打印结果

objc[3473]: OBJC_PRINT_IMAGES: log image and library names as they are loaded
objc[3473]: OBJC_PRINT_IMAGES is set
objc[3473]: OBJC_PRINT_IMAGE_TIMES: measure duration of image loading steps
objc[3473]: OBJC_PRINT_IMAGE_TIMES is set
objc[3473]: OBJC_PRINT_LOAD_METHODS: log calls to class and category +load methods
objc[3473]: OBJC_PRINT_LOAD_METHODS is set
objc[3473]: OBJC_PRINT_INITIALIZE_METHODS: log calls to class +initialize methods
objc[3473]: OBJC_PRINT_INITIALIZE_METHODS is set
objc[3473]: OBJC_PRINT_RESOLVED_METHODS: log methods created by +resolveClassMethod: and +resolveInstanceMethod:
objc[3473]: OBJC_PRINT_RESOLVED_METHODS is set
objc[3473]: OBJC_PRINT_CLASS_SETUP: log progress of class and category setup
objc[3473]: OBJC_PRINT_CLASS_SETUP is set
objc[3473]: OBJC_PRINT_PROTOCOL_SETUP: log progress of protocol setup
objc[3473]: OBJC_PRINT_PROTOCOL_SETUP is set
objc[3473]: OBJC_PRINT_IVAR_SETUP: log processing of non-fragile ivars
objc[3473]: OBJC_PRINT_IVAR_SETUP is set
objc[3473]: OBJC_PRINT_VTABLE_SETUP: log processing of class vtables
objc[3473]: OBJC_PRINT_VTABLE_SETUP is set
objc[3473]: OBJC_PRINT_VTABLE_IMAGES: print vtable images showing overridden methods
objc[3473]: OBJC_PRINT_VTABLE_IMAGES is set
objc[3473]: OBJC_PRINT_CACHE_SETUP: log processing of method caches
objc[3473]: OBJC_PRINT_CACHE_SETUP is set
objc[3473]: OBJC_PRINT_FUTURE_CLASSES: log use of future classes for toll-free bridging
objc[3473]: OBJC_PRINT_FUTURE_CLASSES is set
objc[3473]: OBJC_PRINT_PREOPTIMIZATION: log preoptimization courtesy of dyld shared cache
objc[3473]: OBJC_PRINT_PREOPTIMIZATION is set
objc[3473]: OBJC_PRINT_CXX_CTORS: log calls to C++ ctors and dtors for instance variables
objc[3473]: OBJC_PRINT_CXX_CTORS is set
objc[3473]: OBJC_PRINT_EXCEPTIONS: log exception handling
objc[3473]: OBJC_PRINT_EXCEPTIONS is set
objc[3473]: OBJC_PRINT_EXCEPTION_THROW: log backtrace of every objc_exception_throw()
objc[3473]: OBJC_PRINT_EXCEPTION_THROW is set
objc[3473]: OBJC_PRINT_ALT_HANDLERS: log processing of exception alt handlers
objc[3473]: OBJC_PRINT_ALT_HANDLERS is set
objc[3473]: OBJC_PRINT_REPLACED_METHODS: log methods replaced by category implementations
objc[3473]: OBJC_PRINT_REPLACED_METHODS is set
objc[3473]: OBJC_PRINT_DEPRECATION_WARNINGS: warn about calls to deprecated runtime functions
objc[3473]: OBJC_PRINT_DEPRECATION_WARNINGS is set
objc[3473]: OBJC_PRINT_POOL_HIGHWATER: log high-water marks for autorelease pools
objc[3473]: OBJC_PRINT_POOL_HIGHWATER is set
objc[3473]: OBJC_PRINT_CUSTOM_CORE: log classes with custom core methods
objc[3473]: OBJC_PRINT_CUSTOM_CORE is set
objc[3473]: OBJC_PRINT_CUSTOM_RR: log classes with custom retain/release methods
objc[3473]: OBJC_PRINT_CUSTOM_RR is set
objc[3473]: OBJC_PRINT_CUSTOM_AWZ: log classes with custom allocWithZone methods
objc[3473]: OBJC_PRINT_CUSTOM_AWZ is set
objc[3473]: OBJC_PRINT_RAW_ISA: log classes that require raw pointer isa fields
objc[3473]: OBJC_PRINT_RAW_ISA is set
objc[3473]: OBJC_DEBUG_UNLOAD: warn about poorly-behaving bundles when unloaded
objc[3473]: OBJC_DEBUG_UNLOAD is set
objc[3473]: OBJC_DEBUG_FRAGILE_SUPERCLASSES: warn about subclasses that may have been broken by subsequent changes to superclasses
objc[3473]: OBJC_DEBUG_FRAGILE_SUPERCLASSES is set
objc[3473]: OBJC_DEBUG_NIL_SYNC: warn about @synchronized(nil), which does no synchronization
objc[3473]: OBJC_DEBUG_NIL_SYNC is set
objc[3473]: OBJC_DEBUG_NONFRAGILE_IVARS: capriciously rearrange non-fragile ivars
objc[3473]: OBJC_DEBUG_NONFRAGILE_IVARS is set
objc[3473]: OBJC_DEBUG_ALT_HANDLERS: record more info about bad alt handler use
objc[3473]: OBJC_DEBUG_ALT_HANDLERS is set
objc[3473]: OBJC_DEBUG_MISSING_POOLS: warn about autorelease with no pool in place, which may be a leak
objc[3473]: OBJC_DEBUG_MISSING_POOLS is set
objc[3473]: OBJC_DEBUG_POOL_ALLOCATION: halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools
objc[3473]: OBJC_DEBUG_POOL_ALLOCATION is set
objc[3473]: OBJC_DEBUG_DUPLICATE_CLASSES: halt when multiple classes with the same name are present
objc[3473]: OBJC_DEBUG_DUPLICATE_CLASSES is set
objc[3473]: OBJC_DEBUG_DONT_CRASH: halt the process by exiting instead of crashing
objc[3473]: OBJC_DEBUG_DONT_CRASH is set
objc[3473]: OBJC_DISABLE_VTABLES: disable vtable dispatch
objc[3473]: OBJC_DISABLE_VTABLES is set
objc[3473]: OBJC_DISABLE_PREOPTIMIZATION: disable preoptimization courtesy of dyld shared cache
objc[3473]: OBJC_DISABLE_PREOPTIMIZATION is set
objc[3473]: OBJC_DISABLE_TAGGED_POINTERS: disable tagged pointer optimization of NSNumber et al.
objc[3473]: OBJC_DISABLE_TAGGED_POINTERS is set
objc[3473]: OBJC_DISABLE_TAG_OBFUSCATION: disable obfuscation of tagged pointers
objc[3473]: OBJC_DISABLE_TAG_OBFUSCATION is set
objc[3473]: OBJC_DISABLE_NONPOINTER_ISA: disable non-pointer isa fields
objc[3473]: OBJC_DISABLE_NONPOINTER_ISA is set
objc[3473]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY: disable safety checks for +initialize after fork
objc[3473]: OBJC_DISABLE_INITIALIZE_FORK_SAFETY is set

环境变量列表说明:

变量名 介绍 备注
OBJC_PRINT_OPTIONS list which options are set 输出OBJC已设置的选项
OBJC_PRINT_IMAGES log image and library names as they are loaded 输出已load的image信息
OBJC_PRINT_LOAD_METHODS log calls to class and category +load methods 打印 Class 及 Category 的 + (void)load 方法的调用信息
OBJC_PRINT_INITIALIZE_METHODS log calls to class +initialize methods 打印 Class 的 + (void)initialize 的调用信息
OBJC_PRINT_RESOLVED_METHODS log methods created by +resolveClassMethod and +resolveInstanceMethod: 打印通过 +resolveClassMethod: 或 +resolveInstanceMethod: 生成的类方法
OBJC_PRINT_CLASS_SETUP log progress of class and category setup 打印 Class 及 Category 的设置过程
OBJC_PRINT_PROTOCOL_SETUP log progress of protocol setup 打印 Protocol 的设置过程
OBJC_PRINT_IVAR_SETUP log processing of non-fragile ivars 打印 Ivar 的设置过程
OBJC_PRINT_VTABLE_SETUP log processing of class vtables 打印 vtable 的设置过程
OBJC_PRINT_VTABLE_IMAGES print vtable images showing overridden methods 打印 vtable 被覆盖的方法
OBJC_PRINT_CACHE_SETUP log processing of method caches 打印方法缓存的设置过程
OBJC_PRINT_FUTURE_CLASSES log use of future classes for toll-free bridging 打印从 CFType 无缝转换到 NSObject 将要使用的类(如 CFArrayRef 到 NSArray * )
OBJC_PRINT_GC log some GC operations 打印一些垃圾回收操作
OBJC_PRINT_PREOPTIMIZATION log preoptimization courtesy of dyld shared cache 打印 dyld 共享缓存优化前的问候语
OBJC_PRINT_CXX_CTORS log calls to C++ ctors and dtors for instance variables 打印类实例中的 C++ 对象的构造与析构调用
OBJC_PRINT_EXCEPTIONS log exception handling 打印异常处理
OBJC_PRINT_EXCEPTION_THROW log backtrace of every objc_exception_throw() 打印所有异常抛出时的 Backtrace
OBJC_PRINT_ALT_HANDLERS log processing of exception alt handlers 打印 alt 操作异常处理
OBJC_PRINT_REPLACED_METHODS log methods replaced by category implementations 打印被 Category 替换的方法
OBJC_PRINT_DEPRECATION_WARNINGS warn about calls to deprecated runtime functions 打印所有过时的方法调用
OBJC_PRINT_POOL_HIGHWATER log high-water marks for autorelease pools 打印 autoreleasepool 高水位警告
OBJC_PRINT_CUSTOM_RR log classes with un-optimized custom retain/release methods 打印含有未优化的自定义 retain/release 方法的类
OBJC_PRINT_CUSTOM_AWZ log classes with un-optimized custom allocWithZone methods 打印含有未优化的自定义 allocWithZone 方法的类
OBJC_PRINT_RAW_ISA log classes that require raw pointer isa fields 打印需要访问原始 isa 指针的类
OBJC_DEBUG_UNLOAD warn about poorly-behaving bundles when unloaded 卸载有不良行为的 Bundle 时打印警告
OBJC_DEBUG_FRAGILE_SUPERCLASSES warn about subclasses that may have been broken by subsequent changes to superclasses 当子类可能被对父类的修改破坏时打印警告
OBJC_DEBUG_FINALIZERS warn about classes that implement -dealloc but not -finalize 警告实现了 -dealloc 却没有实现 -finalize 的类
OBJC_DEBUG_NIL_SYNC warn about @synchronized(nil), which does no synchronization 警告 @synchronized(nil) 调用,这种情况不会加锁
OBJC_DEBUG_NONFRAGILE_IVARS capriciously rearrange non-fragile ivars 打印突发地重新布置 non-fragile ivars 的行为
OBJC_DEBUG_ALT_HANDLERS record more info about bad alt handler use 记录更多的 alt 操作错误信息
OBJC_DEBUG_MISSING_POOLS warn about autorelease with no pool in place, which may be a leak 警告没有 pool 的情况下使用 autorelease,可能内存泄漏
OBJC_DEBUG_DUPLICATE_CLASSES halt when multiple classes with the same name are present 当出现类重名时停机
OBJC_USE_INTERNAL_ZONE allocate runtime data in a dedicated malloc zone 在一个专用的 malloc 区分配运行时数据
OBJC_DISABLE_GC force GC OFF, even if the executable wants it on 强行关闭自动垃圾回收,即使可执行文件需要垃圾回收
OBJC_DISABLE_VTABLES disable vtable dispatch 关闭 vtable 分发
OBJC_DISABLE_PREOPTIMIZATION disable preoptimization courtesy of dyld shared cache 关闭 dyld 共享缓存优化前的问候语
OBJC_DISABLE_TAGGED_POINTERS disable tagged pointer optimization of NSNumber et al. optimization of NSNumber et al. <span class="Apple-tab-span" style="white-space:pre"></span> 关闭 NSNumber 等的 tagged pointer 优化
OBJC_DISABLE_NONPOINTER_ISA disable non-pointer isa fields 关闭 non-pointer isa 字段的访问

这些环境变量,我们可以通过Project-> Scheme -> EditScheme -> Run -> Arguments -> Environment Variables 去配置

环境变量设置.jpg

通过OBJC_DISABLE_NONPOINTER_ISAOBJC_PRINT_LOAD_METHODS应用举例一下

OBJC_DISABLE_NONPOINTER_ISA 关闭 non-pointer isa 字段的访问

测试代码

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        FQPerson *person = [FQPerson alloc];
        [person sayHelloWorld];

    }
    return 0;
}

我们在初始化person后加入一个断点 使用lldb调试

首先我们去除 OBJC_DISABLE_NONPOINTER_ISA

(lldb) x/4gx person
0x100752f50: 0x001d800100008735 0x0000000000000000
0x100752f60: 0x0000000000000000 0x0000000000000000
(lldb) p/t 0x001d800100008735
(long) $1 = 0b0000000000011101100000000000000100000000000000001000011100110101

然后我们加上OBJC_DISABLE_NONPOINTER_ISA 设置为YES

(lldb) x/4gx person
0x100a4a300: 0x0000000100008730 0x0000000000000000
0x100a4a310: 0x0000000000000000 0x0000000000000000
(lldb) p/t 0x0000000100008730
(long) $1 = 0b0000000000000000000000000000000100000000000000001000011100110000

在之前,我们研究isa的信息时

iOS底层原理探索 -- isa的本质

我们知道

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \
      uintptr_t has_assoc         : 1;                                       \
      uintptr_t has_cxx_dtor      : 1;                                       \
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
      uintptr_t magic             : 6;                                       \
      uintptr_t weakly_referenced : 1;                                       \
      uintptr_t deallocating      : 1;                                       \
      uintptr_t has_sidetable_rc  : 1;                                       \
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      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
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

isa的第一位就是代表了nonpointer 信息,两次测试最后一位数值不同,正好符合我们的测试的结果

OBJC_PRINT_LOAD_METHODS 打印 Class 及 Category 的 + (void)load 方法的调用信息

同样,我们添加OBJC_PRINT_LOAD_METHODS 并设置值为YES

运行代码 可以得到打印结果

objc[9171]: LOAD: class 'OS_xpc_connection' scheduled for +load

...

objc[9171]: LOAD: category 'NSError(FPAdditions)' scheduled for +load
objc[9171]: LOAD: +[NSError(FPAdditions) load]

objc[9171]: LOAD: class '_DKEventQuery' scheduled for +load
objc[9171]: LOAD: +[_DKEventQuery load]

我们再在我们自己的类中也添加一个+ load方法 运行

objc[9266]: LOAD: class 'OS_xpc_connection' scheduled for +load

...

objc[9266]: LOAD: class '_DKEventQuery' scheduled for +load
objc[9266]: LOAD: +[_DKEventQuery load]

objc[9266]: LOAD: class 'FQPerson' scheduled for +load
objc[9266]: LOAD: +[FQPerson load]

此时,我们可以看到多了我们刚刚添加的我们自己类中的load方法

environ_init()小结

所以我们可以通过environ_init()的配置,做一下指定处理或者消息检索,在我们开发中可以方便我们去进行优化

配置项通过终端命令同样可以查看

$ export OBJC_HELP=1

tls_init()

这里关于线程key的绑定,例如线程数据的析构函数

void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
    pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
    _objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}

static_init()

运行C++静态构造函数。在dyld调用静态构造函数之前,libc调用了objc_init(),所以我们必须自己动手。

static void static_init()
{
    size_t count;
    auto inits = getLibobjcInitializers(&_mh_dylib_header, &count);
    for (size_t i = 0; i < count; i++) {
        inits[i]();
    }
}

runtime_init()

runtime运行时环境初始化

void runtime_init(void)
{
    objc::unattachedCategories.init(32);
    objc::allocatedClasses.init();
}

exception_init()

初始化libobjc的异常处理系统。由map_images()调用。

void exception_init(void)
{
    old_terminate = std::set_terminate(&_objc_terminate);
}

cache_init()

缓存条件的初始化

void cache_init()
{
#if HAVE_TASK_RESTARTABLE_RANGES
    mach_msg_type_number_t count = 0;
    kern_return_t kr;

    while (objc_restartableRanges[count].location) {
        count++;
    }

    kr = task_restartable_ranges_register(mach_task_self(),
                                          objc_restartableRanges, count);
    if (kr == KERN_SUCCESS) return;
    _objc_fatal("task_restartable_ranges_register failed (result 0x%x: %s)",
                kr, mach_error_string(kr));
#endif // HAVE_TASK_RESTARTABLE_RANGES
}

_imp_implementationWithBlock_init()

初始化回调机制。通常情况下,这里不会做什么。所有的初始化都是惰性的,但是对于某些进程,我们急切地加载

void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
    if (__progname &&
        (strcmp(__progname, "QtWebEngineProcess") == 0 ||
         strcmp(__progname, "Steam Helper") == 0)) {
        Trampolines.Initialize();
    }
#endif
}

_dyld_objc_notify_register(&map_images, load_images, unmap_image)

这是我们需要研究的另一个比较关键的方法

//注意:只供objc运行时使用
//注册要在映射、取消映射和初始化objc映像时调用的处理程序。
//Dyld将用一个包含objc图像信息部分的图像数组来调用“mapped”函数。
//那些是dylibs的图像的ref计数将自动被缓冲,因此objc将不再需要
//对它们调用dlopen()以防止它们被卸载。在调用_dyld_objc_notify_register()期间,
//dyld将使用已经加载的objc图像调用“mapped”函数。在以后的任何dlopen()调用中,
//dyld还将调用“mapped”函数。Dyld将在调用Dyld时调用“init”函数
//图像中的初始化器。这时objc调用该映像中的任何+load方法。
void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped);

而在其中的三个参数 mapped init unmapped 分别对应的是三个函数

typedef void (*_dyld_objc_notify_mapped)(unsigned count, const char* const paths[], const struct mach_header* const mh[]);
typedef void (*_dyld_objc_notify_init)(const char* path, const struct mach_header* mh);
typedef void (*_dyld_objc_notify_unmapped)(const char* path, const struct mach_header* mh);

那么问题来了,什么时候调用了map_imagesload_images,也就是
_dyld_objc_notify_mapped_dyld_objc_notify_init 方法

在我们之前研究dyld的流程时,其中我们讲到过一个方法 notifySingle方法

loadImage.jpg

其中sNotifyObjCInit 我们点击跳转一下就能发现

_dyld_objc_notify_init.jpg

就是我们之前提到的load_images方法

notifySingle的调用时机我们之前已经提到过,现在不在赘述。

但通过上面sNotifyObjCInit我们不由联想到map_images 也就是_dyld_objc_notify_mapped我们也可以去搜索sNotifyObjCMapped去了解其调用的时机。

通过全局搜索sNotifyObjCMapped,我们可以发现其被调用的地方在notifyBatchPartial

map_images.jpg

notifyBatchPartial则在registerObjCNotifiers中被调用。

registerObjCNotifiers.jpg

最终入口回到了objc中调用dyld的入口_dyld_objc_notify_register

void _dyld_objc_notify_register(_dyld_objc_notify_mapped    mapped,
                                _dyld_objc_notify_init      init,
                                _dyld_objc_notify_unmapped  unmapped)
{
    dyld::registerObjCNotifiers(mapped, init, unmapped);
}

objcdyld的关联关系大致就在于此。

借用一张图


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

推荐阅读更多精彩内容