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; };
消息的转发
给一个对象调用它不存在的方法,会发生什么?
It works like this: When an object can’t respond to a message because it doesn’t have a method matching the selector in the message, the runtime system informs the object by sending it a
forwardInvocation:
message. Every object inherits aforwardInvocation:
method from theNSObject
class. However,NSObject
’s version of the method simply invokesdoesNotRecognizeSelector:
. By overridingNSObject
’s version and implementing your own, you can take advantage of the opportunity that theforwardInvocation:
message provides to forward messages to other objects
会发生,forwardingInvocation:message, NSObject实现是doesNotRecongizeSelectoer:,所以我们可以重写这个实现。通过消息转发,调用其他类的实现,起到了多重继承的效果
- (void)forwardInvocation:(NSInvocation *)anInvocation { if ([someOtherObject respondsToSelector: [anInvocation selector]]) [anInvocation invokeWithTarget:someOtherObject]; else [super forwardInvocation:anInvocation]; }
替代的对象,如果一个类很大,很复杂,你想懒加载,可以先使用一个替代类进行占位,等到你需要的时候再消息转发。
If you want your objects to act as if they truly inherited the behavior of the objects they forward messages to, you’ll need to re-implement the
respondsToSelector:
andisKindOfClass:
methods to include your forwarding algorithm:
如果再消息转发的情况下,支持某些方法,如何感知,需要重写自己版本的几个函数
- (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 ,否则会形成死循环。
This method gives an object a chance to redirect an unknown message sent to it before the much more expensive
forwardInvocation:
machinery takes over. This is useful when you simply want to redirect messages to another object and can be an order of magnitude faster than regular forwarding. It is not useful where the goal of the forwarding is to capture the NSInvocation, or manipulate the arguments or return value during the forwarding.
调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil:创建一个 NSlnvocation 并传给forwardInvocation:。
This method is used in the implementation of protocols. This method is also used in situations where an
NSInvocation
object must be created, such as during message forwarding. If your object maintains a delegate or is capable of handling messages that it does not directly implement, you should override this method to return an appropriate method signature.
调用forwardInvocation:方法,将第3步获取到的方法签名包装成 Invocation 传入,如何处理就在这里面了,并返回非ni。
调用doesNotRecognizeSelector:,默认的实现是抛出异常。如果第3步没能获得一个方法签名,执行该步骤。
Important
To respond to methods that your object does not itself recognize, you must override
methodSignatureForSelector:
in addition toforwardInvocation:
. The mechanism for forwarding messages uses information obtained frommethodSignatureForSelector:
to create theNSInvocation
object to be forwarded. Your overriding method must provide an appropriate method signature for the given selector, either by pre formulating one or by asking another object for one.
To assist the runtime system, the compiler encodes the return and argument types for each method in a character string and associates the string with the method selector. The coding scheme it uses is also useful in other contexts and so is made publicly available with the
@encode()
compiler directive
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;
}
}