本文完好版共三篇:
- 阿里、字节:一套高效的iOS面试题(一 – runtime 结构模型 – 上)
- 阿里、字节:一套高效的iOS面试题(一 – runtime 结构模型 – 中)
- 阿里、字节:一套高效的iOS面试题(一 – runtime 结构模型 – 下)
3 办法
先看一下办法的结构:
struct method_t {
SEL name;
const char *types;
MethodListIMP imp;
};
简单来说,褪去富丽的外衣,办法由姓名name
、 类型types
, 完成imp
。
OC 一切的办法调用其实都是发音讯,经过 objc_msgSend
完成,能够经过 clang -rewrite-objc main.m -o main.cpp
将 OC 文件转换为 C++ 文件就能够证明这一点,所以要点就在 objc_msgSend
中了。
3.1 办法调用 objc_msgSend
MSG_ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero
#endif
ldr p14, [x0] // p14 = raw isa
GetClassFromIsa_p16 p14, 1, x0 // p16 = class
LGetIsaDone:
// calls imp or objc_msgSend_uncached
CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
b.eq LReturnZero // nil check
GetTaggedClass
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
LReturnZero:
// x0 is already zero
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
ret
END_ENTRY _objc_msgSend
这便是 arm64 环境下 objc_msgSend
的源码,详细解析能够查看 # objc-msg-arm64源码深入分析。
为什么是汇编编写呢?
- 速度快
- 至于任何参数个数,参数类型,回来值类型(C/C++, OC 都做不到)
回到 objc_msgSend
,留意看一下 LGetIsaDone
完成未在 cache 查找到办法 imp 时的完成 __objc_msgSend_uncached
,而它长什么姿态呢?
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
MethodTableLookup ///+ 在办法列表中查找
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
查找 MethodTableLookup
中,再看一下源码:
.macro MethodTableLookup
SAVE_REGS MSGSEND
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
// receiver and selector already in x0 and x1
mov x2, x16
mov x3,zai
bl _lookUpImpOrForward ///+ 调用 lookUpImpOrForward
// IMP in x0
mov x17, x0
RESTORE_REGS MSGSEND
3.2 办法查询
好了,能够看 lookUpImpOrForward
了,传入的 behavior 为 LOOKUP_INITIALIZE | LOOKUP_RESOLVER
:
NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
const IMP forward_imp = (IMP)_objc_msgForward_impcache;
IMP imp = nil;
Class curClass;
lockdebug::assert_unlocked(&runtimeLock);
///+ 若该类还未 initilize 过,则疏忽缓存
if (slowpath(!cls->isInitialized())) {
behavior |= LOOKUP_NOCACHE;
}
runtimeLock.lock();
///+ 经过 allocaltedClasses 检查该类是否已注册
checkIsKnownClass(cls);
///+ 完成该类并履行 +initalize 办法
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
lockdebug::assert_locked(&runtimeLock);
curClass = cls;
///+ 完成该类并履行 +initalize 办法
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
// runtimeLock may have been dropped but is now locked again
lockdebug::assert_locked(&runtimeLock);
curClass = cls;
for (unsigned attempts = unreasonableClassCount();;) {
if (curClass->cache.isConstantOptimizedCache(/* strict */true)) {
#if CONFIG_USE_PREOPT_CACHES
///+ 从缓存中查找办法
imp = cache_getImp(curClass, sel);
if (imp) goto done_unlock;
curClass = curClass->cache.preoptFallbackClass();
#endif
} else {
// curClass method list.
///+ 从本来办法列表中查找 imp
method_t *meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
imp = meth->imp(false);
goto done;
}
///+ 切换为父类。若父类为空则将 imp 替换为 forward
if (slowpath((curClass = curClass->getSuperclass()) == nil)) {
// No implementation found, and method resolver didn't help.
// Use forwarding.
imp = forward_imp;
break;
}
}
// Halt if there is a cycle in the superclass chain.
///+ 若继承链中产生循环,停止履行
if (slowpath(--attempts == 0)) {
_objc_fatal("Memory corruption in class list.");
}
// 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;
}
if (fastpath(imp)) {
// Found the method in a superclass. Cache it in this class.
goto done;
}
}
// No implementation found. Try method resolver once.
///+ 未找到 imp。测验 resolve
///+ 履行 +resolveInstanceMethod 办法
if (slowpath(behavior & LOOKUP_RESOLVER)) {
///+ 去掉 LOOKUP_RESOVELER 符号
behavior ^= LOOKUP_RESOLVER;
return resolveMethod_locked(inst, sel, cls, behavior);
}
done:
if (fastpath((behavior & LOOKUP_NOCACHE) == 0)) { ///+ LOOK_NOCACHE 意味着不要缓存
#if CONFIG_USE_PREOPT_CACHES
///+ 同享缓存
while (cls->cache.isConstantOptimizedCache(/* strict */true)) {
cls = cls->cache.preoptFallbackClass();
}
#endif
///+ 将该办法增加至缓存
log_and_fill_cache(cls, imp, sel, inst, curClass);
}
#if CONFIG_USE_PREOPT_CACHES
done_unlock:
#endif
runtimeLock.unlock();
///+ 若行为符号为 LOOKUP_NIL 且未找到 IMP,则回来空,不进行 forward
if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
return nil;
}
return imp;
}
总结流程即为: 0. 预备操作:完成该类并履行 +initilize 办法
- 沿着继承链(一直到 NSObject)分别以 cache > 办法列表的优先级查找办法
- 若未找到,则按
LOOKUP_RESOLVER
符号测验调用 +resolveMethod 来处理且直接回来 - 找到办法后,将该办法增加至缓存并回来
- 若未找到办法,按
LOOKUP_NIL
回来 nil 或 forward
已然看了查找 imp 的办法,那就趁便看一下其他几个查找的办法:
ALWAYS_INLINE
static IMP _lookUpImpTryCache(id inst, SEL sel, Class cls, int behavior)
{
lockdebug::assert_unlocked(&runtimeLock);
///+ 假如类还未 initialize,则跳转至 loopUpImpOrForward
if (slowpath(!cls->isInitialized())) {
// see comment in lookUpImpOrForward
return lookUpImpOrForward(inst, sel, cls, behavior);
}
///+ 从缓存中查找
IMP imp = cache_getImp(cls, sel);
if (imp != NULL) goto done;
#if CONFIG_USE_PREOPT_CACHES
if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
imp = cache_getImp(cls->cache.preoptFallbackClass(), sel);
}
#endif
///+ 若在缓存中未找到,则跳转至 loopUpImpOrForward
if (slowpath(imp == NULL)) {
return lookUpImpOrForward(inst, sel, cls, behavior);
}
done:
///+ 若行为符号为 LOOKUP_NIL 且未找到 IMP,则回来空
if ((behavior & LOOKUP_NIL) && imp == (IMP)_objc_msgForward_impcache) {
return nil;
}
return imp;
}
IMP lookUpImpOrForwardTryCache(id inst, SEL sel, Class cls, int behavior)
{
return _lookUpImpTryCache(inst, sel, cls, behavior);
}
IMP lookUpImpOrNilTryCache(id inst, SEL sel, Class cls, int behavior)
{
///+ 增加行为符号 LOOKUP_NIL(只查找实在 IMP 或 nil)
return _lookUpImpTryCache(inst, sel, cls, behavior | LOOKUP_NIL);
}
先看一下办法查找的进程:
static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
lockdebug::assert_locked(&runtimeLock);
///+ 从该类中获取办法列表
auto const methods = cls->data()->methods();
///+ 遍历每个办法列表,进行查找
for (auto mlists = methods.beginLists(),
end = methods.endLists();
mlists != end;
++mlists)
{
method_t *m = search_method_list_inline(*mlists, sel);
if (m) return m;
}
return nil;
}
ALWAYS_INLINE static method_t *
search_method_list_inline(const method_list_t *mlist, SEL sel)
{
int methodListIsFixedUp = mlist->isFixedUp();
int methodListHasExpectedSize = mlist->isExpectedSize();
if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
///+ 假如办法现已被排序过(load_image 中),则二分查找
return findMethodInSortedMethodList(sel, mlist);
} else {
///+ 不然线性查找
if (auto *m = findMethodInUnsortedMethodList(sel, mlist))
return m;
}
return nil;
}
- 在类中查找办法时,会从
instance.isa.data.rwe
中取出一切的办法列表数组(二维数组) - 然后遍历一切的办法列表,依据该类自身在完成时的情况(每个办法是否已依照办法名排序)进行二分查找或线性查找。
3.3 动态派发
再看一下 resolveMethod_locked
:
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
lockdebug::assert_locked(&runtimeLock);
runtimeLock.unlock();
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
///+ 一般类则调用 [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else {
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
///+ 元类则先调用 [nonMetaClass resolveClassMethod:sel] ,
resolveClassMethod(inst, sel, cls);
if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
///+ 元类中未找到,则调用寻找实例办法
resolveInstanceMethod(inst, sel, cls);
}
}
// chances are that calling the resolver have populated the cache
// so attempt using it
///+ 再次查找并缓存本来的 imp
///+ 这儿答应找到 forward
return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
- 假如是调用实例办法,则调用
+resolveInstanceMehtod:
测验处理 - 假如是调用类办法,先调用
+resolveClassMehtod:
测验处理,若未处理则调用+resolveInstanceMethod:
测验处理。
假如该类不是元类(调用实例办法时),经过 resolveInstanceMethod
测验 resolve
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
lockdebug::assert_unlocked(&runtimeLock);
SEL resolve_sel = @selector(resolveInstanceMethod:);
///+ 在类中查找 +resolveInstanceMethod:
///+ 将类作为实例在元类中查找:榜首个参数为 cls,第三个参数为元类
///+ 由于 +resolveInstanceMethod: 是类办法
if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
// Resolver not implemented.
///+ 未完成 +resolveInstanceMethod:
///+ 一般不会走到这儿,NSObject 中有默许完成
return;
}
///+ 若类完成了 +resolveInstanceMethod: ,则将原始 sel 作为参数调用
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 或 nil
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
}
- 首要保证类是否完成在
+resolveInstanceMethod:
办法,假如未完成则直接 return(但 NSObject 中会有默许完成) - 调用
+resolveInstanceMethod:
(能够再该办法内动态增加一个 IMP) - 再次经过本来的 sel 查找 IMP 并缓存(假如在第二步中增加了对应的 IMP 则能够找到)
若该类是元类(调用类办法),经过 resolveClassMethod
> resolveInstanceMethod
的优先级测验 resolve:
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
lockdebug::assert_unlocked(&runtimeLock);
///+ 首要测验在元类中查找缓存 resolveClassMethod: 办法的结果
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
///+ 未完成 +resolveClassMethod:
///+ 但 NSObject 中有默许完成
return;
}
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
///+ 获取原始的类目标,由于 +resolveClassMethod: 音讯是要发送给类目标的
///+ 里边有个特别情况,关于根类来说,他的元类便是自身,比如 NSObject
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if (!nonmeta->isRealized()) {
_objc_fatal(...);
}
}
///+ 调用 +resolveClassMethod:
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Cache the result (good or bad) so the resolver doesn't fire next time.
// +resolveClassMethod adds to self->ISA() a.k.a. cls
///+ 再次查找并缓存本来的 imp
///+ 这儿仅查找实在 IMP 或 nil
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
...
打印
...
}
大致逻辑与 +resolveInstanceMethod:
相同,有一点比较重要。这儿额定做了一个获取原始类目标的操作,由于传入的 cls 可能是类目标,也可能是元类目标,而 +resolveClassMethod:
这个音讯是要发送给类目标,而不是元类目标。(只不过最终是在元类中查找该办法,但音讯仍是发送给原始类目标的)
从 resolveMethod_nolock
中 cls 是元类来看,调用一个类的类办法时,若该类办法未完成且没有完成 +resolveClassMethod:
,最终会调用到根类的同名实例办法。
为什么是根类呢?由于根类的元类便是其自身。验证一下:
@interface SomeObject : NSObject
+ (void)sameMethod;
@end
@@implementation SomeObject
@end
@interface NSObject (Cat)
@end
@@implementation NSObject (Cat)
- (void)sameMethod {
NSLog(@"履行了实例办法:%s", __FUNCTION__);
}
@end
测验调用一下 [SomeObject sameMethod]
看下结果。
当然,除了 objc_msgSend
外,还有其他好几个类似的办法,比较特别且重要的便是 objc_msgSendSuper
。先对比下两者的揭露定义:
id _Nullable objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
id _Nullable objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
很明显,除了办法名之外,榜首个参数(也便是音讯接纳者)不同。看一眼 objc_super
是什么:
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
///+ 音讯接纳者
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
///+ 指定接纳音讯的特定父类,是继承链的直接父类
__unsafe_unretained _Nonnull Class super_class;
};
一个误区
为一个什么都没有的类增加一个 init
办法:
@implementation SomeObject
- (instancetype)init {
// self = [super init]; // 为了让代码更少,这行暂时不要
NSStringFromClass([super class]));
return self;
}
@end
运用 clang -rewrite-objc SomeObject.m -o SomeObject.cpp
重写成 C++,只看 找到该办法的定义:
static instancetype _I_SomeObject_init(SomeObject * self, SEL _cmd) {
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("SomeObject"))}, sel_registerName("class"));
return self;
}
仔细看一下 objc_msgSenSuper
的榜首个参数是什么?
(__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("SomeObject"))}
改写一下:
(struct objc_super){
self,
SomeObject.superclass
}
此刻,音讯接受者其实依然是 self
,也便是调用途的这个目标自身。只不过榜首音讯响应类不是 SomeObject。
所以,这儿在查找办法时,完全能够改写成方才咱们看过的 lookUpImpOrForward
格局
lookUpImpOrForward(self, @selector(class), SomeObject.superclass, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
此刻接纳音讯的依然是 self。所以这儿拿到的依然是 SomeObject,而不是它的父类。
3.4 音讯转发
在 3.1 办法调用 查找办法时,有一个 _objc_msgForward_impcache
。在未找到对应 sel 的办法且 behavior 未符号为 LOOKUP_NIL 时的回来。
从字面意思看,音讯转发。还在是 objc-msg-arm64.s 中,依然是汇编编写:
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
///+ 无结构体特别版本
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
///+ 继续到这儿
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
好了,能够去找 __objc_forward_handler
了:
__attribute__ ((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)",
class_isMetaClass(object_getClass(self)) ? '+' : '-',
object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
void objc_setForwardHandler(void *fwd, void *fwd_stret)
{
_objc_forward_handler = fwd;
}
先看一下 objc_defaultForwarHandler
办法内部完成。unrecognized selector sent to instance,嗯嗯这个提示很熟悉,调用未完成的办法便是这报错。
再看一眼下边的 objc_setForwarHandler
,跟 uncaught_handler
一样,咱们只需设置好这个 handler 就能够愉快地转发音讯了。
BUT~当我设置之后,并打印不出来任何堆栈信息。看来 Apple 搞了幺蛾子
已然这儿搞不定,那就回到 Apple 揭露的手法上:
- (id)forwardingTargetForSelector:(SEL)aSelector;
+ (id)forwardingTargetForSelector:(SEL)aSelector;
一者为实例办法,二者为类办法,都是为了找一个“替死鬼”来处理这个音讯,例如这样:
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector("unimplementedMethod") {
return otherObject;
}
return [super forwardingTargetForSelector:aSelector];
}
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector("unimplementedMethod") {
return OtherClass;
}
return [super forwardingTargetForSelector:aSelector];
}
假如这儿没有“替死鬼”回来了 nil,还有最终一次机会:runtime 会向目标 -methodSignatureForSelector:
音讯,先拿到办法的完好签名生成 NSMethodInvocation
,然后将其包装成一个 NSInvocation
,然后再向目标发送 forwardInvocation:
音讯,咱们就能够处理这个 invocation 了:
- (void)forwardInvocation:(NSInvocation *)invocation;
+ (void)forwardInvocation:(NSInvocation *)invocation;
依然是实例办法与类办法共存:
- (void)forwardInvocation:(NSInvocation *)invocation
if ([invocation selector] == @selector(unimplementedMethod)) {
[invocation invokeWithTarget:otherObject];
return;
}
return [super forwardInvocation:invocation];
}
+ (void)forwardInvocation:(NSInvocation *)invocation
if ([invocation selector] == @selector(unimplementedMethod)) {
[invocation invokeWithTarget:otherClass];
return;
}
return [super forwardInvocation:invocation];
}
好了总结一下:
- objc_msgSend
- NilOrTagged
- CacheLookup
- MethodTableLookup
- lookUpImpOrForward /// 找到 IMP 则调用
- resolveMethod,回调到 5
- forwardingTargetForSelector /// 找到“替死鬼”就让它处理
- methodSignatureForSelector
- forwardInvocation /// 让下一个“替死鬼”处理,没找到就溃散
或者看一下大佬的图
4 其他
4.1 class_rw_t
与 class_ro_t
首要从姓名上就能看出来,class_rw_t
是可读可写的,class_ro_t
是只读的。
先看一下 class_ro_t
:
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart; ///+ 实例其实地址
uint32_t instanceSize; ///+ 实例占用空间巨细
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout; ///+ 实例变量布局。这表明成员变量在目标内存空间内占用的内存块
Class nonMetaclass; ///+ 非元类
};
explicit_atomic<const char *> name; ///+ 类名
WrappedPtr<method_list_t, method_list_t::Ptrauth> baseMethods; ///+ 编译期间就确认的办法(不包括分类中的)
protocol_list_t * baseProtocols; ///+ 编译期间就确认的协议
const ivar_list_t * ivars; ///+ 成员变量列表
const uint8_t * weakIvarLayout; ///+ 弱引证成员变量布局。这表明弱引证成员变量在目标内存空间内占用的内存块
property_list_t *baseProperties; ///+ 编译期间就确认的特点
...
...
}
再看一眼 class_rw_t
struct class_rw_t {
uint32_t flags;
uint16_t witness;
uint16_t index; ///+ realizeClassWithoutSwift 中经过 chooseClassArrayIndex 指定
explicit_atomic<uintptr_t> ro_or_rw_ext; ///+ 指针联合体:class_ro_t 或者 class_rw_ext_t
Class firstSubclass; //+ 子类
Class nextSiblingClass; //+ 兄弟类
}
很明显,没有太多要点,那看一下 class_rw_rext_t
:
struct class_rw_ext_t {
DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
class_ro_t_authed_ptr<const class_ro_t> ro; ///+ class_ro_t 指针。realizeClassWithoutSwift 中拓荒 rw 时设置
method_array_t methods; ///+ 一切办法列表
property_array_t properties; ///+ 一切特点
protocol_array_t protocols; ///+ 一切特点
const char *demangledName; ///+ 姓名
uint32_t version;
};
这儿仅仅看一下这些结构体罢了,真实初始化是在 2 类是如何被加载的 中的。
4.2 目标
id
是一个通用 OC 目标。那么 id
又是什么呢?
typedef struct objc_object *id;
struct objc_object {
private:
char isa_storage[sizeof(isa_t)];
...
...
}
也便是说,id
便是一个 objc_object
结构体指针,其内部仅存储一个 isa_t
结构体(可查看 **1.2 isa)。
再看一下类:
typedef struct objc_class *Class;
struct objc_class : objc_object {
// Class ISA;
Class superclass; /// 父类指针
cache_t cache; /// 缓存该类现已用过的函数指针
class_data_bits_t bits; /// 存储该类的办法、特点、遵循的协议列表等
}
也便是说,类自身也是一个目标。
其实,OC 中万物是目标,block、protocol 均为目标。
看看转移来的 描绘上述结构体之间联系的类图 :
这便是 OC 中各类结构体之间的联系。
4.3 metaclass
什么是 metaclass
? 2 类是如何被加载的 有这样一些代码:
///+ 在 realizeClassWithSwift 办法中
///+ 完成其元类
metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
cls->initClassIsa(metacls);
///+ 在 load_categories_nolock 办法中
///+ 将协议中的类办法,类特点附加到元类中
if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties)) {
attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
}
能够看到,class 的 isa 指向其元类,而协议中的类办法与类特点是增加到元类中的。再结合 3 办法调用,在进行办法查找时,实例办法在类的办法列表中查找,类办法在元类的办法列表中查找。这两者查找办法是不是完全相同,都是在 instance.isa.data.methods
中进行查找,两者一致了。
所以,元类的存在便是为了音讯转发机制的统一性而设计的。
4.4 load
与 initialize
+load
在 2 类是如何加载的 中有一个流程 load_image,其首要效果便是履行 +load
办法。
该流程是在 main 函数之前履行的,它会查找一切类与分类的 +load
办法并履行,先类再分类。
并且是依照 父类 > 子类 的次序递归履行的,也便是说,+load
办法最好不要调用 super,假如调用了那么父类中的流程将会履行很屡次。
关于没有继承联系的类来说,其 +load
的调用次序取决于 Compile Sources 的排列次序。category 的 +load
办法的调用次序也是如此。
+initialize
而在 3 办法 中有一个流程 lookUpImpOrForward,其首要效果是查找办法的 imp。
其中有这样一句:cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE);
,这一个句的首要效果便是调用 +initialize
办法。
来看一下 realizeAndInitializeIfNeeded_locked
:
static Class
realizeAndInitializeIfNeeded_locked(id inst, Class cls, bool initialize)
{
lockdebug::assert_locked(&runtimeLock);
///+ 假如类还未完成,则完成它。内部调用 realizeClassWithoutSwift
if (slowpath(!cls->isRealized())) {
cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
}
if (slowpath(initialize && !cls->isInitialized())) {
///+ 初始化该类
cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
// If sel == initialize, class_initialize will send +initialize and
// then the messenger will send +initialize again after this
// procedure finishes. Of course, if this is not being called
// from the messenger then it won't happen.
///+ 假如当时接纳到的音讯便是 +initialize,再完成该流程之后会再次发送 +initialize 音讯。
}
return cls;
}
嗯,继续往里边走
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
return initializeAndMaybeRelock(cls, obj, lock, true);
}
再往里边走:
static Class initializeAndMaybeRelock(Class cls, id inst,
mutex_t& lock, bool leaveLocked)
{
lockdebug::assert_locked(&lock);
///+ 假如现已初始化过了,则疏忽
if (cls->isInitialized()) {
if (!leaveLocked) lock.unlock();
return cls;
}
// Find the non-meta class for cls, if it is not already one.
// The +initialize message is sent to the non-meta class object.
///+ 获取原始的类
Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// Realize the non-meta class if necessary.
if (nonmeta->isRealized()) {
///+ nometa 是一个类,现已被完成了,所以这不需要做任何事情
lock.unlock();
} else {
///+ 假如该类还没完成,那么就完成它:内部会调用 realizeClassWithoutSwift
nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
///+ 获取 nometa 的类
cls = object_getClass(nonmeta);
}
///+ 初始化这个原始的类,调用 +initialize
initializeNonMetaClass(nonmeta);
if (leaveLocked) runtimeLock.lock();
return cls;
}
看一下简化版的 initializeNonMetaClass
:
void initializeNonMetaClass(Class cls)
{
Class supercls;
bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
///+ 来初始化该类之前,一定要保证父类现已完成初始化操作
supercls = cls->getSuperclass();
if (supercls && !supercls->isInitialized()) {
///+ 递归调用,传入父类
initializeNonMetaClass(supercls);
}
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
///+ 符号该类正在初始化 RW_INITIALIZING
cls->setInitializing();
reallyInitialize = YES;
}
if (reallyInitialize) {
// We successfully set the CLS_INITIALIZING bit. Initialize the class.
///+ 现已成功将该类符号为正在初始化。履行初始化操作。
// Record that we're initializing this class so we can message it.
///+ 记载当时线程正在初始化该类
_setThisThreadIsInitializingClass(cls);
///+ 在答应多进程 fork() 履行的情况下,子线程完成后能够直接符号为已初始化
if (MultithreadedForkChild) {
// LOL JK we don't really call +initialize methods after fork().
performForkChildInitialize(cls, supercls);
return;
}
// Exceptions: A +initialize call that throws an exception
// is deemed to be a complete and successful +initialize.
///+ 抛出反常的 +initialize 调用才被认为是一次完好且成功的的
@try
{
///+ 调用 +initialize。
///+ 这儿发送 @selector(initialize) 音讯,会再走一次这个流程
callInitialize(cls);
}
@catch (...) {
@throw;
}
@finally
{
// Done initializing.
///+ 完成初始化操作
lockAndFinishInitializing(cls, supercls);
}
return;
}
else if (cls->isInitializing()) {
...
}
else if (cls->isInitialized()) {
...
}
else {
...
}
}
从上边源码能够看到,在调用某个类的 +initialize
之前,会先调用其父类的 +initialize
办法。
好,回想一下: lookUpImpOrForward
是在接纳音讯时才会走到的流程,这就意味着 +initialize
是在类接纳音讯才会调用的。
另外,在将分类附加到的进程中,会将分类中的一切办法插入到类办法列表的最前边。这就意味着分类中的 +initialize
办法会掩盖类自身的 +initialize
办法,其掩盖次序也取决的他们在 Compile Sources 中的次序,后者掩盖前者。
4.5 一些可能会用的 runtime 办法
依照 runtime.h 排序:
function | note |
---|---|
Class object_getClass(id obj) |
传入目标获取其类,传入类目标则获取元类 |
Class object_setClass(id obj, Class cls) |
设置目标的类型,回来本来的类型,KVO 便是这样经过替换 isa 完成的 |
BOOL object_isClass(id obj) |
判别传入目标是否是类 |
Class objc_getClass(const char *name) |
依据字符串获取类 |
Class objc_getMetaClass(char *name) |
依据字符串获取元类 |
char *class_getName(Class cls) |
获取类名 |
BOOL class_isMetaClass(Class cls) |
判别传入目标是否是元类 |
Class class_getSuperclass(Class cls) |
获取类的父类 |
size_t class_getInstanceSize(Class cls) |
获取该类实例占用的内存巨细 |
Ivar *class_copyIvarList(Class cls, unsigned int *outCount) |
获取该类的成员变量列表 |
Method class_getInstanceMethod(Class cls, SEL name) |
依据 SEL 获取实例办法的 method 结构体 |
Method class_getClassMethod(Class cls, SEL name) |
依据 SEL 获取类办法的 method 结构体 |
IMP class_getMethodImplementation(Class cls, SEL name) |
依据 SEL 获取实力办法的详细完成 IMP |
BOOL class_respondsToSelector(Class name, SEL name) |
判别该类实例目标是否能响应指定办法 |
Method *class_copyMethodList(Class cls, unsigned int *outCount) |
获取类的实例办法列表 |
BOOL class_conformsToProtocol(Class cls, Protocol *protocol) |
判别类是否契合指定协议 |
Protocol **class_copyProtocolList(Class cls, unsigned int *outCount) |
获取类契合的协议列表 |
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount) |
获取类的特点列表 |
BOOL class_addMethod(Class cls, SEL name, IMP imp, char *types) |
向类中增加一个办法 |
IMP class_replaceMethod(Class cls, SEL name, IMP imp, char *types) |
替换办法的完成 |
Class objc_allocateClassPair(Class superclass, char *name, size_t extraBytes) |
动态创立一个新的类及其元类,KVO 用到 |
void objc_registerClassPair(Class cls) |
向 runtime 动态注册由 objc_allocateClassPair 创立的类,KVO 用到 |
Class objc_duplicateClass(Class original, char *name, size_t extraBytes) |
依据原始类,新类名以及额定空间动态创立一个新类,KVO 用到,不要直接调用 |
void objc_disposeClassPair(Class cls) |
销毁一个类及其元类,必须是 objc_allocateClassPair 创立的类,KVO 用到 |
SEL method_getName(Method m) |
获取指定 method 的姓名 |
IMP method_getImplementation(Method m) |
获取 method 的详细完成 |
char *method_getTypeEncoding(Method m) |
获取 method 的类型 |
unsigned int method_getNumberOfArguments(Method m) |
获取 method 的参数数量 |
char *method_copyReturnType(Method m) |
获取 method 的回来类型 |
char *method_copyArgumentType(Method m, unsigned int index) |
获取 method 指定下标参数的类型 |
void method_getReturnType(Methhod m, char *dst, size_t dst_len) |
获取 method 的回来类型,同 method_copyReturnType
|
void method_getArgumentType(Method m, unsigned int index,char *dst, size_t dst_len) |
获取 method 指定下边参数的类型,同 method_copyArgumentType
|
IMP method_setImplementation(Method m, IMP imp) |
设置 method 的详细完成,回来本来的完成 |
void method_exchangeImplementations(Method m1, Method m2) |
交流两个 method 的详细完成 |
BOOL protocol_conformsToProtocol(Protocol *proto, Protocol *other) |
判别 proto 是否契合 other |
IMP imp_implementationWithBlock(id block) |
经过 block 创立一个 IMP |
id imp_getBlock(IMP anImp) |
获取由 imp_implementationWithBlock 创立时的 block |
BOOL imp_removeBlock(IMP anImp) |
开释由 imp_implementationWithBlock 创立时的 block |
id objc_loadWeak(id location) |
获取弱引证指向的原始目标 |
id objc_storeWeak(id *localtion, id obj) |
将一个新的弱引证指针存储到列表中 |
void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy) |
增加相关目标 |
id objc_getAssociatedObject(id object, void * key) |
获取对应 key 的相关目标 |
void objc_removeAssociatedObjects(id object) |
移除一切相关目标 |