Runtime 是什么?

objective-C会把函数调用的转换为消息发送,objc_MsgSend(receiver, msg), 注意,recevier指的是消息的接受者。那么self, super的区别是什么?其实 super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者!他们两个的不同点在于:super 会告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。不管调用[self class]还是[super class],接受消息的对象都是当前 self指向的实例。在代码中使用self可以获取recevier,使用_cmd获取method。

1.SEL 的含义:

在前面我们看到方法选标 SEL 的定义为:

它是一个指向 objc_selector 指针,表示方法的名字/签名。

typedef struct objc_selector   *SEL;   

2. IMP 的含义:

在前面我们也看到 IMP 的定义为:

typedef id (*IMP)(id, SEL, ...);

根据前面id 的定义,我们知道 id是一个指向 objc_object 结构体的指针,该结构体只有一个成员isa,所以任何继承自 NSObject 的类对象都可以用id 来指代,因为 NSObject 的第一个成员实例就是isa。

至此,我们就很清楚地知道 IMP  的含义:IMP 是一个函数指针,这个被指向的函数包含一个接收消息的对象id(self  指针), 调用方法的选标 SEL (方法名),以及不定个数的方法参数,并返回一个id。也就是说 IMP 是消息最终调用的执行代码,是方法真正的实现代码 。我们可以像在C语言里面一样使用这个函数指针

3. Method是,包含了方法名称,与实现的结构。

typedef struct objc_method *Method;

typedef struct objc_ method {

    SEL method_name;

    char *method_types;

    IMP method_imp;

};

消息的转发

给一个对象调用它不存在的方法,会发生什么?

会发生,forwardingInvocation:message, NSObject实现是doesNotRecongizeSelectoer:,所以我们可以重写这个实现。通过消息转发,调用其他类的实现,起到了多重继承的效果

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}

替代的对象,如果一个类很大,很复杂,你想懒加载,可以先使用一个替代类进行占位,等到你需要的时候再消息转发。

如果再消息转发的情况下,支持某些方法,如何感知,需要重写自己版本的几个函数

- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* Here, test whether the aSelector message can *
* be forwarded to another object and whether that *
* object can respond to it. Return YES if it can. */
}
return NO;
}

如果一直找到根类都没有找到对应的方法,object_msgForward就作为替代IMP,并执行

objc_msgForward消息转发做的几件事:

调用resolveInstanceMethod:方法 (或resolveClassMethod:)。允许用户在此时为该 Class 动态添加实现。如果有实现了,则调用并返回YES,那么重新开始objc_msgSend流程。这一次对象会响应这个选择器,一般是因为它已经调用过class_addMethod。如果仍没实现,继续下面的动作。

调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回非 nil 对象。否则返回 nil ,继续下面的动作。注意,这里不要返回 self ,否则会形成死循环。

调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil:创建一个 NSlnvocation 并传给forwardInvocation:。

调用forwardInvocation:方法,将第3步获取到的方法签名包装成 Invocation 传入,如何处理就在这里面了,并返回非ni。

调用doesNotRecognizeSelector:,默认的实现是抛出异常。如果第3步没能获得一个方法签名,执行该步骤。

Objects are treated like structures. For example, passing the NSObject class name to @encode() yields this encoding:, NSObject就只有一个isa指针

{NSObject=#}

之前用MJExtension在多线程下一直崩溃,后来MJExtension修复了,就是加了一个同步, https://github.com/CoderMJLee/MJExtension/issues/518

+ (instancetype)cachedTypeWithCode:(NSString *)code

{

MJExtensionAssertParamNotNil2(code, nil);

@synchronized (self) {

MJPropertyType *type = types_[code];

if (type == nil) {

type = [[self alloc] init];

type.code = code;

types_[code] = type;

}

return type;

}

}

05-11 15:19