这篇文章主要分析_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();
tls_init();
static_init();
runtime_init();
exception_init();
cache_init();
_imp_implementationWithBlock_init();
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
#if __OBJC2__
didCallDyldNotifyRegister = true;
#endif
}
-
environ_init
环境变量初始化
主要是读取当前的环境变量,这些环境变量可以在Xcode中设置,Edit Scheme -> Arguments -> Environment Variables
借助终端指令查看所有的环境变量:
export OBJC_HELP=1
/Applications/Safari.app/Contents/MacOS/Safari
环境变量表:
变量名 | 介绍 | 备注 |
---|---|---|
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_IMAGE_TIMES | measure duration of image loading steps | image加载的持续时间 |
OBJC_PRINT_LOAD_METHODS | log calls to class and category +load methods | 打印class 以及 category中+load方法的调用信息 |
OBJC_PRINT_INITIALIZE_METHODS | log calls to class +initialize methods | 打印class中+initialize方法的调用信息 |
OBJC_PRINT_RESOLVED_METHODS | log methods created by +resolveClassMethod: and +resolveInstanceMethod: | 打印通过resolveInstanceMethod和resolveClassMethod生成的方法 |
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 | 打印ivars的设置过程 |
OBJC_PRINT_VTABLE_SETUP | log processing of class vtables | 打印class vtables设置过程 |
OBJC_PRINT_VTABLE_IMAGES | print vtable images showing overridden methods | 打印vtable images 被覆盖的方法 |
OBJC_PRINT_CACHE_SETUP | log processing of method caches | 打印cache设置过程 |
OBJC_PRINT_FUTURE_CLASSES | log use of future classes for toll-free bridging | 打印从 CFType 无缝转换到 NSObject 将要使用的类(如 CFArrayRef 到 NSArray * ) |
OBJC_PRINT_PREOPTIMIZATION | log preoptimization courtesy of dyld shared cache | 打印共享缓存预优化礼貌用语 |
OBJC_PRINT_CXX_CTORS | log calls to C++ ctors and dtors for instance variables | 打印实例对象Cxx 构造方法和析构方法 |
OBJC_PRINT_EXCEPTIONS | log exception handling | 打印异常处理 |
OBJC_PRINT_EXCEPTION_THROW | log backtrace of every objc_exception_throw() | 打印异常抛出时的堆栈 |
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 | og high-water marks for autorelease pools | 打印autorelease pools高水位警告 |
OBJC_PRINT_CUSTOM_CORE | log classes with custom core methods | 打印拥有自定义核心方法的类 |
OBJC_PRINT_CUSTOM_RR | log classes with custom retain/release methods | 打印拥有自定义retain/release方法的类 |
OBJC_PRINT_CUSTOM_AWZ | log classes with 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 | 卸载不良行为的bundles时打印警告 |
OBJC_DEBUG_FRAGILE_SUPERCLASSES | warn about subclasses that may have been broken by subsequent changes to superclasses | 子类可能对父类修改破坏打印警告 |
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_POOL_ALLOCATION | halt when autorelease pools are popped out of order, and allow heap debuggers to track autorelease pools | 当自动释放池无序弹出时停止,并允许堆调试器跟踪自动释放池 |
OBJC_DEBUG_DUPLICATE_CLASSES | halt when multiple classes with the same name are present | 出现重复的类时停止 |
OBJC_DEBUG_DONT_CRASH | halt the process by exiting instead of crashing | 进程停止时通过退出代替崩溃 |
OBJC_DISABLE_VTABLES | disable vtable dispatch | 关闭vtable分发 |
OBJC_DISABLE_PREOPTIMIZATION | disable preoptimization courtesy of dyld shared cache | 关闭共享缓存预优化礼貌用语 |
OBJC_DISABLE_TAGGED_POINTERS | disable tagged pointer optimization of NSNumber et al. | 关闭tagged pointer指针优化 |
OBJC_DISABLE_TAG_OBFUSCATION | disable obfuscation of tagged pointers | 关闭 tagged pointers 的混淆 |
OBJC_DISABLE_NONPOINTER_ISA | disable non-pointer isa fields | 关闭non-pointer isa指针优化 |
OBJC_DISABLE_INITIALIZE_FORK_SAFETY | disable safety checks for +initialize after fork | fork后禁用+initialize 的安全检查 |
-
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++静态构造函数
调用C++静态构造函数,libc
调用_objc_init
是在dyld
加载静态构造函数之前,所以这里我们必须自己做。
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()
初始化unattachedCategories
和allocatedClasses
void runtime_init(void)
{
objc::unattachedCategories.init(32);
objc::allocatedClasses.init();
}
-
exception_init()
初始化objc
的异常回调
初始化old_terminate
,发生异常时调用old_terminate
里的方法
void exception_init(void)
{
old_terminate = std::set_terminate(&_objc_terminate);
}
static void (*old_terminate)(void) = nil;
static void _objc_terminate(void)
{
if (PrintExceptions) {
_objc_inform("EXCEPTIONS: terminating");
}
if (! __cxa_current_exception_type()) {
// No current exception.
(*old_terminate)();
}
else {
// There is a current exception. Check if it's an objc exception.
@try {
__cxa_rethrow();
} @catch (id e) {
// It's an objc object. Call Foundation's handler, if any.
(*uncaught_handler)((id)e);
(*old_terminate)();
} @catch (...) {
// It's not an objc object. Continue to C++ terminate.
(*old_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()
Mac_OS_OSX环境下,初始化Trampolines
。此函数主要是启动回调函数,通常不做什么,因为所有的初始化都是惰性的,但是对于某些进程,要迫不及待的加载libobjc-trampolines.dylib
void
_imp_implementationWithBlock_init(void)
{
#if TARGET_OS_OSX
// Eagerly load libobjc-trampolines.dylib in certain processes. Some
// programs (most notably QtWebEngineProcess used by older versions of
// embedded Chromium) enable a highly restrictive sandbox profile which
// blocks access to that dylib. If anything calls
// imp_implementationWithBlock (as AppKit has started doing) then we'll
// crash trying to load it. Loading it here sets it up before the sandbox
// profile is enabled and blocks it.
//
// This fixes EA Origin (rdar://problem/50813789)
// and Steam (rdar://problem/55286131)
if (__progname &&
(strcmp(__progname, "QtWebEngineProcess") == 0 ||
strcmp(__progname, "Steam Helper") == 0)) {
Trampolines.Initialize();
}
#endif
}
_dyld_objc_notify_register
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
主要向dyld
注册这三个函数,map_images、 load_images、 unmap_image
1.map_images
:管理文件和动态库中所有的符号
2.load_images
:加载执行load
方法
3.unmap_image
:dyld
取消映射的image
进程
下面我们会着重分析map_images
和load_images
。
map_images
map_images
void
map_images(unsigned count, const char * const paths[],
const struct mach_header * const mhdrs[])
{
mutex_locker_t lock(runtimeLock);
return map_images_nolock(count, paths, mhdrs);
}
map_images_nolock
void
map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
....已省略部分代码.....
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
//初始化之前的一些操作
if (firstTime) {
preopt_init();
}
//类的计数,根据数目调整各种表格大小
// Find all images with Objective-C metadata.
hCount = 0;
// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[i];
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) { ... }
if (mhdr->filetype == MH_EXECUTE) { ... }
hList[hCount++] = hi;
if (PrintImages) { ... }
}
}
if (firstTime) {
//初始化一些方法名,注册cxx_construct和cxx_destruct
sel_init(selrefCount);
// AutoreleasePoolPage::init();
//SideTablesMap.init();
//_objc_associations_init();
arr_init();
}
//读所有的镜像文件
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[i]);
}
}
}
我们主要分析_read_images
方法。
-
_read_images
_read_images
的代码比较多,我们首先讲一下它主要做了什么事情,等下再分段探索。
1.条件控制进行一次的加载
2.修复预编译阶段@selector的混乱问题
3.错误混乱的类处理
4.修复重映射一些没有被镜像文件加载进来的类
5.修复一些消息
6.当我们的类里有协议的时候 readProtocol
7.修复没有被加载的协议
8.分类处理
9.类的加载处理
10.没有被处理的类 优化那些被侵犯的类
1. 条件控制进行一次的加载
if (!doneOnce) {
doneOnce = YES;
launchTime = YES;
//主要给DisableNonpointerIsa赋值
#if SUPPORT_NONPOINTER_ISA
// Disable non-pointer isa under some conditions.
# if TARGET_OS_OSX
// Disable non-pointer isa if the app is too old
// (linked before OS X 10.11)
if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("");
}
}
// Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
// New apps that load old extensions may need this.
for (EACH_HEADER) {
if (hi->mhdr()->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
DisableNonpointerIsa = true;
if (PrintRawIsa) {
_objc_inform("");
}
}
break; // assume only one MH_EXECUTE image
}
# endif
#endif
//当DisableTaggedPointers为true时,给一些mask和shift赋值
if (DisableTaggedPointers) {
disableTaggedPointers();
}
//给objc_debug_taggedpointer_obfuscator赋值 针对小对象处理的
initializeTaggedPointerObfuscator();
//创建了一张表,用来放类
// namedClasses
// Preoptimized classes don't go in this table.
// 4/3 is NXMapTable's load factor
int namedClassesSize =
(isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
gdb_objc_realized_classes =
NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
ts.log("IMAGE TIMES: first time tasks");
}
- 给
DisableNonpointerIsa
赋值 - 当
DisableTaggedPointers
为true
时,给一些mask
和shift
赋值 - 给
objc_debug_taggedpointer_obfuscator
赋值 针对小对象处理的 - 创建一张表,用来放类
2. 修复预编译阶段@selector的混乱问题
static size_t UnfixedSelectors;
{
mutex_locker_t lock(selLock);
for (EACH_HEADER) {
if (hi->hasPreoptimizedSelectors()) continue;
bool isBundle = hi->isBundle();
SEL *sels = _getObjc2SelectorRefs(hi, &count);
UnfixedSelectors += count;
for (i = 0; i < count; i++) {
const char *name = sel_cname(sels[i]);
SEL sel = sel_registerNameNoLock(name, isBundle);
if (sels[i] != sel) {
sels[i] = sel;// 将相同的函数,使他们`sel`的地址相等。
}
}
}
}
遍历Mach-o
的方法,注册到内存,相同的sel
地址赋值相同。
3. 错误混乱的类处理
bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
for (EACH_HEADER) {
if (! mustReadClasses(hi, hasDyldRoots)) {
// Image is sufficiently optimized that we need not call readClass()
continue;
}
classref_t const *classlist = _getObjc2ClassList(hi, &count);
bool headerIsBundle = hi->isBundle();
bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
for (i = 0; i < count; i++) {
Class cls = (Class)classlist[i];
//前面获取的cls只是一个地址,在`readClass`之后才有相关信息
Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
if (newCls != cls && newCls) {
// Class was moved but not deleted. Currently this occurs
// only when the new class resolved a future class.
// Non-lazily realize the class below.
resolvedFutureClasses = (Class *)
realloc(resolvedFutureClasses,
(resolvedFutureClassCount+1) * sizeof(Class));
resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
}
}
}
类被移动但是并没有删除才会走到这里,一般情况不会做任何处理,这里不做研究。这里有一个值得关注的函数,就是readClass
。cls的指针经过readClass
之后就会关联上一些信息,比如类名。
3.1 readClass
Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
{
const char *mangledName = cls->mangledName();
//当前类没有superclass才会走,防错处理
if (missingWeakSuperclass(cls)) {
if (PrintConnecting) {
_objc_inform("CLASS: IGNORING class '%s' with "
"missing weak-linked superclass",
cls->nameForLogging());
}
addRemappedClass(cls, nil);
cls->superclass = nil;
return nil;
}
//swift类处理
cls->fixupBackwardDeployingStableSwift();
//以后可能会被用到的类,保留rw数据
Class replacing = nil;
if (Class newCls = popFutureNamedClass(mangledName)) {
// This name was previously allocated as a future class.
// Copy objc_class to future class's struct.
// Preserve future's rw data block.
if (newCls->isAnySwift()) {
_objc_fatal("Can't complete future class request for '%s' "
"because the real class is too big.",
cls->nameForLogging());
}
class_rw_t *rw = newCls->data();
const class_ro_t *old_ro = rw->ro();
memcpy(newCls, cls, sizeof(objc_class));
rw->set_ro((class_ro_t *)newCls->data());
newCls->setData(rw);
freeIfMutable((char *)old_ro->name);
free((void *)old_ro);
addRemappedClass(cls, newCls);
replacing = cls;
cls = newCls;
}
//主要走addNamedClass和addClassTableEntry这两个方法
if (headerIsPreoptimized && !replacing) {
// class list built in shared cache
// fixme strict assert doesn't work because of duplicates
// ASSERT(cls == getClass(name));
ASSERT(getClassExceptSomeSwift(mangledName));
} else {
addNamedClass(cls, mangledName, replacing);
addClassTableEntry(cls);
}
// for future reference: shared cache never contains MH_BUNDLEs
if (headerIsBundle) {
cls->data()->flags |= RO_FROM_BUNDLE;
cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
}
return cls;
}
我们自定义类主要走addNamedClass
和addClassTableEntry
这两个方法。
addNamedClass
static void addNamedClass(Class cls, const char *name, Class replacing = nil)
{
runtimeLock.assertLocked();
Class old;
//用类名去查找类,找到了说明他是元类,通过NXMapInsert把元类插入表中
//没有找到,说明表中没这个类,就把name 和 cls 添加到映射中,在这之后就可以查找到这个类
if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
inform_duplicate(name, old, cls);
addNonMetaClass(cls);
} else {
NXMapInsert(gdb_objc_realized_classes, name, cls);
}
ASSERT(!(cls->data()->flags & RO_META));
}
这个方法实际上是吧 name
和cls
添加到非元类的映射中。
addClassTableEntry
static void
addClassTableEntry(Class cls, bool addMeta = true)
{
runtimeLock.assertLocked();
// This class is allowed to be a known class via the shared cache or via
// data segments, but it is not allowed to be in the dynamic table already.
auto &set = objc::allocatedClasses.get();
ASSERT(set.find(cls) == set.end());
if (!isKnownClass(cls))
set.insert(cls);
if (addMeta)
addClassTableEntry(cls->ISA(), false);
}
把一个类加到所有类的列表中,如果addMeta
为真也会自动添加元类。
4. 修复重映射一些没有被镜像文件加载进来的类
if (!noClassesRemapped()) {
for (EACH_HEADER) {
Class *classrefs = _getObjc2ClassRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
// fixme why doesn't test future1 catch the absence of this?
classrefs = _getObjc2SuperRefs(hi, &count);
for (i = 0; i < count; i++) {
remapClassRef(&classrefs[i]);
}
}
}
修复重映射的类,类列表和非懒加载的类保持未重新隐射。类引用和超级引用被重新映射用于消息调度。正常情况也不会走。
5. 修复一些消息
// Fix up old objc_msgSend_fixup call sites
for (EACH_HEADER) {
message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
if (count == 0) continue;
if (PrintVtables) {
_objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
"call sites in %s", count, hi->fname());
}
for (i = 0; i < count; i++) {
fixupMessageRef(refs+i);
}
}
旧objc
才会进来。
6. 当我们的类里有协议的时候 readProtocol
bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
// Discover protocols. Fix up protocol refs.
for (EACH_HEADER) {
extern objc_class OBJC_CLASS_$_Protocol;
Class cls = (Class)&OBJC_CLASS_$_Protocol;
ASSERT(cls);
NXMapTable *protocol_map = protocols();
bool isPreoptimized = hi->hasPreoptimizedProtocols();
// Skip reading protocols if this is an image from the shared cache
// and we support roots
// Note, after launch we do need to walk the protocol as the protocol
// in the shared cache is marked with isCanonical() and that may not
// be true if some non-shared cache binary was chosen as the canonical
// definition
if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
if (PrintProtocols) {
_objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
hi->fname());
}
continue;
}
bool isBundle = hi->isBundle();
protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
for (i = 0; i < count; i++) {
readProtocol(protolist[i], cls, protocol_map,
isPreoptimized, isBundle);
}
}
7. 修复没有被加载的协议
for (EACH_HEADER) {
// At launch time, we know preoptimized image refs are pointing at the
// shared cache definition of a protocol. We can skip the check on
// launch, but have to visit @protocol refs for shared cache images
// loaded later.
if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
continue;
protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
for (i = 0; i < count; i++) {
remapProtocolRef(&protolist[i]);
}
}
8. 分类处理
发现类别,仅在初始化类别之后进行操作。对于启动时发现的类别,这一步操作被推迟到_dyld_objc_notify_register
调用完成后的第一个 load_images
调用。
if (didInitialAttachCategories) {
for (EACH_HEADER) {
load_categories_nolock(hi);
}
}
9. 类的加载处理
for (EACH_HEADER) {
classref_t const *classlist =
_getObjc2NonlazyClassList(hi, &count);
for (i = 0; i < count; i++) {
Class cls = remapClass(classlist[i]);
if (!cls) continue;
addClassTableEntry(cls);
if (cls->isSwiftStable()) {
if (cls->swiftMetadataInitializer()) {
_objc_fatal("Swift class %s with a metadata initializer "
"is not allowed to be non-lazy",
cls->nameForLogging());
}
// fixme also disallow relocatable classes
// We can't disallow all Swift classes because of
// classes like Swift.__EmptyArrayStorage
}
realizeClassWithoutSwift(cls, nil);
}
}
10.没有被处理的类 优化那些被侵犯的类
if (resolvedFutureClasses) {
for (i = 0; i < resolvedFutureClassCount; i++) {
Class cls = resolvedFutureClasses[i];
if (cls->isSwiftStable()) {
_objc_fatal("Swift class is not allowed to be future");
}
realizeClassWithoutSwift(cls, nil);
cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
}
free(resolvedFutureClasses);
}
map_images的调用流程
上篇文章我们讲了load_images
是在镜像库初始化时调用的,这里我们探索一下map_images
在哪调用?
-
打开
dyld
源码,全局搜索sNotifyObjCMapped
,在上篇文章我们了解到sNotifyObjCMapped = map_images
。
在notifyBatchPartial
函数中找到了sNotifyObjCMapped
执行的地方。
-
全局搜索
notifyBatchPartial
我们发现在registerObjCNotifiers
函数中,赋值之后已经开始调用notifyBatchPartial
。也就是说map_images
其实调用在load_images
之前。
深入研究
先准备好代码:
@interface LRPerson : NSObject
@property (nonatomic,strong) NSString *name;
@property (nonatomic,strong) NSString *icon;
- (void)sayHello;
- (void)sayHappy;
- (void)instanceMethod;
+ (void)classMethod;
@end
@implementation LRPerson
//- (void)sayHello {
// NSLog(@"%@",_cmd);
//}
- (void)sayHappy {
NSLog(@"%@",_cmd);
}
- (void)instanceMethod {
NSLog(@"%@",_cmd);
}
+ (void)classMethod {
NSLog(@"%@",_cmd);
}
@end
添加一个categary
@protocol LRPersonDelegate
- (void)delegateMethod;
@end
NS_ASSUME_NONNULL_BEGIN
@interface LRPerson (LR)
- (void)instanceMethod;
+ (void)classMethod;
@end
@implementation LRPerson (LR)
- (void)instanceMethod {
NSLog(@"category----%s",__func__);
}
+ (void)classMethod {
NSLog(@"category----%s",__func__);
}
@end
在我们要研究的方法里添加一下代码,方便研究我们创建的类。
const char *mangledName = cls->mangledName();
const char *className = "LRPerson";
if (strcmp(mangledName, className) == 0){
printf("来了 \n");
}
类的加载
这里我们主要研究类的加载部分,在函数中加上以上代码,并打上断点,运行程序。
然而事情超出了预料,这个断点并没有走。那到底是为什么呢?
阅读上方的注释,发现有一个非懒加载类,我们猜测只有非懒加载类才会走这个方法。
- 懒加载类和非懒加载类
这个很好区分,实现了+load 方法就是非懒加载类。
除此之外还有其他两种情况:
1.子类实现了+load
2.category
实现了+load
懒加载类: 没有实现+load方法,在第一次对其发送消息之前才初始化。
好处:降低启动时间,减少启动工作量。项目中有成千上万的类,没有必要启动时全部加载。
意识到了问题之后,我们给LRPerson类实现+load
方法,再次运行代码。断点果然进来了
addClassTableEntry
在前面讲过,主要是把类加入所有类的表里,元类也会添加。
下面重点分析realizeClassWithoutSwift
realizeClassWithoutSwift
static Class realizeClassWithoutSwift(Class cls, Class previously)
{
runtimeLock.assertLocked();
class_rw_t *rw;
Class supercls;
Class metacls;
if (!cls) return nil;
if (cls->isRealized()) return cls;
ASSERT(cls == remapClass(cls));
//设置类的rw 和 ro
auto ro = (const class_ro_t *)cls->data();
auto isMeta = ro->flags & RO_META;
if (ro->flags & RO_FUTURE) {
// This was a future class. rw data is already allocated.
rw = cls->data();
ro = cls->data()->ro();
ASSERT(!isMeta);
cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
} else {
//正常类走这里
//设置rw的数据ro flags
//cls 设置 rw
rw = objc::zalloc();
rw->set_ro(ro);
rw->flags = RW_REALIZED|RW_REALIZING|isMeta;
cls->setData(rw);
}
#if FAST_CACHE_META
//是元类时 设置cache 的 bit
if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif
//为这个类选择一个索引,如果没有可用的索引则设置它的cls->instancesRequireRawIsa
cls->chooseClassArrayIndex();
if (PrintConnecting) {
_objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
cls->nameForLogging(), isMeta ? " (meta)" : "",
(void*)cls, ro, cls->classArrayIndex(),
cls->isSwiftStable() ? "(swift)" : "",
cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
}
//递归处理父类和元类
supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
#if SUPPORT_NONPOINTER_ISA
if (isMeta) {
//进来的是元类 对cache->bit 赋值
cls->setInstancesRequireRawIsa();
} else {
bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
bool rawIsaIsInherited = false;
static bool hackedDispatch = false;
if (DisableNonpointerIsa) {
instancesRequireRawIsa = true;
}
else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
{
hackedDispatch = true;
instancesRequireRawIsa = true;
}
else if (supercls && supercls->superclass &&
supercls->instancesRequireRawIsa())
{
instancesRequireRawIsa = true;
rawIsaIsInherited = true;
}
if (instancesRequireRawIsa) {
cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
}
}
// SUPPORT_NONPOINTER_ISA
#endif
// 更新它的元类和父类
cls->superclass = supercls;
cls->initClassIsa(metacls);
// 协调实例变量偏移/布局。
// 这可能会重新分配 class_ro_t,更新我们的 ro 变量。
if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
//设置setInstanceSize
cls->setInstanceSize(ro->instanceSize);
//设置有C++的构造方法和析构方法
if (ro->flags & RO_HAS_CXX_STRUCTORS) {
cls->setHasCxxDtor();
if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
cls->setHasCxxCtor();
}
}
if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
(supercls && supercls->forbidsAssociatedObjects()))
{
rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
}
//将此类连接到他的父类的子类列表
if (supercls) {
addSubclass(supercls, cls);
} else {
addRootClass(cls);
}
//处理类的方法列表 协议列表 属性列表
methodizeClass(cls, previously);
return cls;
}
在这里会从Mach-o
文件中取出类的ro
,复制出一个rw
,再把rw
设置给当前类。如果当前类有父类或者元类,会递归处理父类和元类。还会做一下工作:
- 进来的类是元类时,设置cache ->setBit
- 为这个类选择一个索引,如果没有可用的索引则设置它的
cls->instancesRequireRawIsa
- 设置
setInstanceSize、 setHasCxxDtor() 、setHasCxxCtor()
- 链接完成父类和子类的列表
- 处理类的方法列表 协议列表 属性列表
我们接下来研究他的主要方法methodizeClass
。
methodizeClass
static void methodizeClass(Class cls, Class previously)
{
runtimeLock.assertLocked();
bool isMeta = cls->isMetaClass();
auto rw = cls->data();
auto ro = rw->ro();
auto rwe = rw->ext();
// Methodizing for the first time
if (PrintConnecting) {
_objc_inform("CLASS: methodizing class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
//获取方法列表 按照sel的值排序 增序
//如果存在rwe 则在rwe添加这些方法
method_list_t *list = ro->baseMethods();
if (list) {
prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
if (rwe) rwe->methods.attachLists(&list, 1);
}
//如果存在rwe 则在rwe添加property_list
property_list_t *proplist = ro->baseProperties;
if (rwe && proplist) {
rwe->properties.attachLists(&proplist, 1);
}
//如果存在rwe 则在rwe添加protocol_list
protocol_list_t *protolist = ro->baseProtocols;
if (rwe && protolist) {
rwe->protocols.attachLists(&protolist, 1);
}
//当进来的是根元类时,添加initialize方法
if (cls->isRootMetaclass()) {
addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
}
// Attach categories.
if (previously) {
if (isMeta) {
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_METACLASS);
} else {
// When a class relocates, categories with class methods
// may be registered on the class itself rather than on
// the metaclass. Tell attachToClass to look for those.
objc::unattachedCategories.attachToClass(cls, previously,
ATTACH_CLASS_AND_METACLASS);
}
}
objc::unattachedCategories.attachToClass(cls, cls,
isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
#if DEBUG
// Debug: sanity-check all SELs; log method list contents
for (const auto& meth : rw->methods()) {
if (PrintConnecting) {
_objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
cls->nameForLogging(), sel_getName(meth.name));
}
ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name);
}
#endif
}
我们看一下prepareMethodLists
排序方法。
prepareMethodLists
static void
prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
bool baseMethods, bool methodsFromBundle)
{
runtimeLock.assertLocked();
if (addedCount == 0) return;
if (baseMethods) {
ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
}
for (int i = 0; i < addedCount; i++) {
method_list_t *mlist = addedLists[i];
ASSERT(mlist);
//方法在这里排序
if (!mlist->isFixedUp()) {
fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
}
}
if (cls->isInitialized()) {
objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
}
}
fixupMethodList
static void
fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
{
runtimeLock.assertLocked();
ASSERT(!mlist->isFixedUp());
if (!mlist->isUniqued()) {
mutex_locker_t lock(selLock);
// Unique selectors in list.
for (auto& meth : *mlist) {
const char *name = sel_cname(meth.name);
meth.name = sel_registerNameNoLock(name, bundleCopy);
}
}
// 根据sel的地址排序
if (sort) {
//排序
method_t::SortBySELAddress sorter;
std::stable_sort(mlist->begin(), mlist->end(), sorter);
}
// 设置mlist->setFixedUp() 已经排序了
mlist->setFixedUp();
}
SortBySELAddress
struct SortBySELAddress :
public std::binary_function
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{ return lhs.name < rhs.name; }
};
在以下地方打上断点,分别打印mlist,对比排序前后的顺序。
在打印排序后的sel对应的二进制和16进制大小
对比发现,是根据
sel
增序排列的。
非懒加载类的加载到这里就差不多完成了。
懒加载类
懒加载类是在第一次发送消息时才加载的。我们修改一下代码去掉+ load
方法,在main
函数里发送一次消息。
在realizeClassWithoutSwift
加入以下代码,打上断点。
auto lr_ro = (const class_ro_t *)cls->data();
bool lr_isMeta = lr_ro->flags & RO_META;
const char *mangledName = cls->mangledName();
const char *className = "LRPerson";
if (strcmp(mangledName, className) == 0 && !lr_isMeta ){
method_list_t *list = lr_ro->baseMethods();
printf("来了 \n");
}
进了断点之后,bt
打印当前堆栈信息
进入realizeClassWithoutSwift
流程是:
1.dyld start
2.进入main
3.objc_alloc(cls=LRPerson)
4.callAlloc
5._objc_msgSend_uncached
6.lookUpImpOrForward
7.initializeAndLeaveLocked
8.initializeAndMaybeRelock
9.realizeClassMaybeSwiftAndUnlock
10.realizeClassMaybeSwiftMaybeRelock
11.realizeClassWithoutSwift
看到这个不知道大家有没有觉得熟悉,这正是之前消息查找流程之中的一个分支。
- 搜
lookUpImpOrForward
这两个方法正是lookUpImpOrForward
里的slowpath
方法