-
Notifications
You must be signed in to change notification settings - Fork 4
load
load
作为 Objective-C 中的一个方法,与其它方法有很大的不同。它只是一个在整个文件被加载到运行时,在 main
函数调用之前被 ObjC 运行时调用的钩子方法。
实现这个方法可以让我们在类加载的时候执行一些类相关的行为。子类的 load
方法会在它的所有父类的 load
方法之后执行,而分类的 load
方法会在它的主类的 load
方法之后执行。但是不同的类之间的 load
方法的调用顺序是不确定的。
-
调用时机:
-
load
方法会在runtime加载类、分类时调用, 每个类、分类的load
,在程序运行过程中只调用一次
-
-
调用顺序:
- 先执行父类中的
load
方法 - 先执行主类中的
load
方法 - 再执行分类中的
load
方法,按着编译的反顺序,越后编译越先被执行
- 先执行父类中的
当有多个分类时,每个分类都重写原类中的一个方法时,那程序调用这个方法的时候就会按编译文件的顺序来判断,谁在最后就调用谁(可以通过项目设置中的Build Phases-->Compile Sources中调整)
原理:是将分类中的方法加入到了之前对象方法列表数组的前面了,所有找方法的时候会先找到分类中的方法
在libobjc
中_objc_init
函数中调用了dyld
的_dyld_objc_notify_register
函数,并将load_images
传给dyld
中以sNotifyObjCInit
回调函数的方式存储起来
-
objc-os.mm
中_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异常处理系统初始化 exception_init(); // 缓存条件初始化 cache_init(); // 启动回调机制。通常不会做什么,因为所有的初始化都是惰性的 _imp_implementationWithBlock_init(); /* _dyld_objc_notify_register -- dyld 注册的地方 - 仅供objc运行时使用 - 注册处理程序,以便在映射、取消映射 和初始化objc镜像文件时使用,dyld将使用包含objc_image_info的镜像文件数组,回调 mapped 函数 map_images:dyld将image镜像文件加载进内存时,会触发该函数 load_images:dyld初始化image会触发该函数 unmap_image:dyld将image移除时会触发该函数 */ _dyld_objc_notify_register(&map_images, load_images, unmap_image); #if __OBJC2__ didCallDyldNotifyRegister = true; #endif }
-
通过
objc
源码中_objc_init
源码实现,进入load_images
的源码实现void load_images(const char *path __unused, const struct mach_header *mh) { if (!didInitialAttachCategories && didCallDyldNotifyRegister) { didInitialAttachCategories = true; loadAllCategories(); } // Return without taking locks if there are no +load methods here. if (!hasLoadMethods((const headerType *)mh)) return; recursive_mutex_locker_t lock(loadMethodLock); // Discover load methods { mutex_locker_t lock2(runtimeLock); prepare_load_methods((const headerType *)mh); } // Call +load methods (without runtimeLock - re-entrant) call_load_methods(); } void call_load_methods(void) { static bool loading = NO; bool more_categories; loadMethodLock.assertLocked(); // Re-entrant calls do nothing; the outermost call will finish the job. if (loading) return; loading = YES; void *pool = objc_autoreleasePoolPush(); do { // 1. Repeatedly call class +loads until there aren't any more while (loadable_classes_used > 0) { call_class_loads(); } // 2. Call category +loads ONCE more_categories = call_category_loads(); // 3. Run more +loads if there are classes OR more untried categories } while (loadable_classes_used > 0 || more_categories); objc_autoreleasePoolPop(pool); loading = NO; }
可以发现
call_load_methods
函数其核心是通过do-while
循环调用类call_class_loads
的函数或分类的call_category_loads
函数 -
call_class_loads
源码实现/*********************************************************************** * call_class_loads * Call all pending class +load methods. * If new classes become loadable, +load is NOT called for them. * * Called only by call_load_methods(). **********************************************************************/ static void call_class_loads(void) { int i; // Detach current loadable list. struct loadable_class *classes = loadable_classes; int used = loadable_classes_used; loadable_classes = nil; loadable_classes_allocated = 0; loadable_classes_used = 0; // Call all +loads for the detached list. for (i = 0; i < used; i++) { Class cls = classes[i].cls; load_method_t load_method = (load_method_t)classes[i].method; if (!cls) continue; if (PrintLoading) { _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging()); } (*load_method)(cls, @selector(load)); } // Destroy the detached list. if (classes) free(classes); }
-
call_category_loads
源码实现/*********************************************************************** * call_category_loads * Call some pending category +load methods. * The parent class of the +load-implementing categories has all of * its categories attached, in case some are lazily waiting for +initalize. * Don't call +load unless the parent class is connected. * If new categories become loadable, +load is NOT called, and they * are added to the end of the loadable list, and we return TRUE. * Return FALSE if no new categories became loadable. * * Called only by call_load_methods(). **********************************************************************/ static bool call_category_loads(void) { int i, shift; bool new_categories_added = NO; // Detach current loadable list. struct loadable_category *cats = loadable_categories; int used = loadable_categories_used; int allocated = loadable_categories_allocated; loadable_categories = nil; loadable_categories_allocated = 0; loadable_categories_used = 0; // Call all +loads for the detached list. for (i = 0; i < used; i++) { Category cat = cats[i].cat; load_method_t load_method = (load_method_t)cats[i].method; Class cls; if (!cat) continue; cls = _category_getClass(cat); if (cls && cls->isLoadable()) { if (PrintLoading) { _objc_inform("LOAD: +[%s(%s) load]\n", cls->nameForLogging(), _category_getName(cat)); } (*load_method)(cls, @selector(load)); cats[i].cat = nil; } } // Compact detached list (order-preserving) shift = 0; for (i = 0; i < used; i++) { if (cats[i].cat) { cats[i-shift] = cats[i]; } else { shift++; } } used -= shift; // Copy any new +load candidates from the new list to the detached list. new_categories_added = (loadable_categories_used > 0); for (i = 0; i < loadable_categories_used; i++) { if (used == allocated) { allocated = allocated*2 + 16; cats = (struct loadable_category *) realloc(cats, allocated * sizeof(struct loadable_category)); } cats[used++] = loadable_categories[i]; } // Destroy the new list. if (loadable_categories) free(loadable_categories); // Reattach the (now augmented) detached list. // But if there's nothing left to load, destroy the list. if (used) { loadable_categories = cats; loadable_categories_used = used; loadable_categories_allocated = allocated; } else { if (cats) free(cats); loadable_categories = nil; loadable_categories_used = 0; loadable_categories_allocated = 0; } if (PrintLoading) { if (loadable_categories_used != 0) { _objc_inform("LOAD: %d categories still waiting for +load\n", loadable_categories_used); } } return new_categories_added; }
通过源码和注释得到被调用的正是类的
+load
方法 -
load
的执行过程是:- _dyld_start
- dyldbootstrap::start
- dyld::_main
- dyld::initializeMainExecutable
- ImageLoader::runInitializers
- ImageLoader::processInitializers
- ImageLoader::recursiveInitialization
- dyld::notifySingle(是一个回调处理)
- sNotifyObjCInit
- load_images(libobjc.A.dylib)
+ [xxx load]
-
懒加载类 (数据加载推迟到第一次消息的时候)
lookUpImpOrForward
realizeClassMaybeSwiftMaybeRelock
relizeClassWithoutSwift
methodizeClass
-
非懒加载类(
map_images
的时候 加载所有数据)readClass
_getObjc2NonlazyClassList
realizeClassWithoutSwift
methodizeClass
-
懒加载类 与 懒加载分类
其中
realizeClassMaybeSwiftMaybeRelock
是消息流程中慢速查找中有的函数,即在第一次调用消息时才有的函数 -
非懒加载类 与 非懒加载分类
map_images -> map_images_nolock -> _read_images -> readClass -> _getObjc2NonlazyClassList -> realizeClassWithoutSwift -> methodizeClass -> attachToClass
, 此时的mlists是一维数组,然后走到load_images部分load_images --> loadAllCategories -> load_categories_nolock -> attachCategories -> attachLists
,此时的mlists是二维数组 -
非懒加载类 与 懒加载分类
即主类实现了+load方法,分类未实现+load方法
类 和 分类的加载是在
read_images
就加载数据了, 其中data数据在编译时期就已经完成了 -
懒加载类 与 非懒加载分类
懒加载类 + 非懒加载分类的数据加载,只要分类实现了
load
,会迫使主类提前加载,即 主类 强行转换为 非懒加载类主类 分类 类加载方式 类加载情况 分类加载情况 load load non-lazy 类在 map_images
加载分类在 load_images
加载load - non-lazy 类在 map_images
加载分类方法已经通过 mach-o
读取到ro
里- load non-lazy 类在 map_images
加载分类方法已经通过 mach-o
读取到ro
里- - lazy 类在第一次消息转发时加载 分类方法已经通过 mach-o
读取到ro
里
补充
- 主类存在多个分类, 只要有一个分类实现了
load
方法,就会迫使主类提前加载,变成非懒加载类 - 处理重名方法和排序时,先检查SEL的name(字符串地址),然后根据imp地址排序
- 如果类和分类有同名方法
- 如果是普通方法,则会调用分类中的重名方法
- 如果是
+load
方法,则先调用类中的+load
,在依次调用分类的+load
类的加载-分类的加载load
方法调用后,加载一个类所有的工作都已经完成了
ShenYj.github.io - 简书地址 - 返回首页
-
Apple
Common
蓝牙
LBS
音视频
- AVAudioSessionchange_route
- 切换线路
- StreamingKit
- Audio Unit 基础
OC 与 Swift 混编
Object-C
- 代码混淆
- autoreleasepool
- 忽略编译器(clang)警告
- 定时器
- 锁
- RunLoop
- block
- NS_REFINED_FOR_SWIFT
- NS_CLOSED_ENUM
- NS_TYPED_ENUM、NS_STRING_ENUM
- NS_TYPED_EXTENSIBLE_ENUM、NS_EXTENSIBLE_STRING_ENUM
- 关键字nonnull和nullable
- class、objc_getClass和object_getclass方法区别
- isKindOfClass和isMemberOfClass
- 应用程序的加载
- non-lazy classes & lazy classes
- load方法
- initialize方法
- 方法的本质
- 类型编码
- self和super
- 类的内存分析
Swift
- precondition
- 权限控制
- Array常用Api
- String初始化、定义
- String常用Api
- String截取演练
- Set定义、创建
- Set访问和修改
- Dictionary操作
- Dictionary和KeyValuePairs
- Dictionary与String转换
- 常用高阶函数
- enum原始值
- enum关联值
- enum遍历
- 递归enum
- enum内存分配
- 指针
- for循环
- break跳出循环
- 变量名与关键字冲突
- 类的定义
- 类的继承和初始化
- 关键字: final
- 关键字: mutating
- 关键字: lazy
- 修饰类方法的关键字: static
- 关键字: final、dynamic、objc和_dynamicReplacement
- 关键字:@dynamicMemberLookup和@dynamicCallable
- 关键字: propertyWrapper
- 自定义运算符
- 下标: subscript
- 扩展: extension
- 协议: protocol
- 协议和扩展
- 为什么需要泛型
- 泛型函数定义
- 泛型类型
- 泛型的类型约束
- 关联类型
- 为泛型定义要求
- 泛型下标
- 多线程
- Attributes
- 错误处理
- Codable
- DispatchSourceTimer
- Swift 5.x 演练: 更多功能编辑页
- Swift 5.x 类库收集
- 单元测试笔记
- 实例对象内存结构
- 元类型、Type、Self
- frozen
- convention
- Swift(5.3.2)源码编译
- SQLite.Swift类库演练
- Swift 5.5 关键字: async/await
- Swift 5.5 新特性: Continuations
- Swift 5.5 新特性: Actor
- Swift 方法调度
- Swift Mirror
- Swift 关键字: @_silgen_name
- Swift 关键字: @_disfavoredOverload
- swiftmodule
- Swift 5.6 新特性: Type placeholders
- Swift 5.6 新特性: #unavailable
- Swift 5.6 新特性: CodingKeyRepresentable
- Swift 5.6 新特性: existential any
- Swift 5.7 新特性: if-let/guard 语法简化
- Swift 5.7 新特性: Multi-statement closure type inference
- Swift 5.8 新特性: @backDeployed
- Swift 5.9 新特性: if switch expressions
- Swift 6.0 新特性:@preconcurrency
RxSwift
macOS - AppKit
-
iOS Assembly(ARM64)
-
C++
C++ 基础
- cout、cin
- 函数重载
- 默认参数
- extern "C"
- pragma once
- inline function
- const
- Reference
- 汇编
- 类和对象
- 堆空间内存管理
- Constructor
- Destructor
- 成员变量初始化
- 声明与实现分离
- namespace
- 继承
- 访问权限
- 初始化列表
- 多态:虚函数
- 多态:虚函数实现原理
- 多态:虚析构函数
- 多态:纯虚函数
- 多态:抽象类
- 多继承
- static
- static: 单例模式
- const 成员
- 引用类型成员
- 拷贝构造函数
- 调用父类的拷贝构造函数
- 浅拷贝、深拷贝
- 对象型参数和返回值
- 匿名对象
- 隐式构造
- 编译器自动生成的构造函数
- 友元
- 内部类
- 局部类
- 运算符重载
- 模板
- 类型转换
- C++标准
- Lambda
- 异常
- 智能指针
-
Flutter
Dart
Flutter
-
Go
Go 基础
-
Ruby
Ruby 基础
-
React-Native
React-Native
-
工具篇
-
Swift Package Manager
-
自动化
-
TroubleShooting
-
扩展