修仙的道路不需要敌人
Runtime渐渐清醒过来,毕竟两瓶啤酒,没有多大劲,它怀疑的眼光看的我很不自在。我说:“大兄弟,你喝醉的时候,都是我一直陪着你。”它问我:“趁我喝醉,你没套我什么话吧?”以我的脾气,况且现在我还知道了它的弱点,我当然不会怂:“讲道理,套你话是必须的了。”我们对视了不到一秒,它笑着对我说:“哪有什么秘密,想知道什么,我都告诉你。”看来它是被我的坦率征服了。
我买了一包辣条,沏了一壶茶,听Runtime讲述它的故事。
动态方法解析:
听Runtime讲,有时候会遇到@dynamic propertyName;
这种招式,破解方法就是通过resolveInstanceMethod
动态的提供属性的相关方法。一个OC方法调用,至少有self和_cmd这两个参数,模版大概如下:
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation....
}
然后这样使用resolveInstanceMethod
:
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(needDynamicCallMethod)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
一个类在消息转发机制开始之前是有机会去做动态方法解析的。也就是说在调用了一个未知的方法后,我们可以像上面的代码那样,在resolveInstanceMethod:
中给类添加这个未知方法,使方法的调用顺利完成。如果在resolveInstanceMethod:
中返回了NO,那么将启动消息转发机制。
注: 这种方案的前提是dynamicMethodIMP
函数已经被提前定义。
消息转发:
“曾今有一份真挚的消息摆在我面前,我没有珍惜,等程序崩溃的时候,我才后悔莫及。人世间最痛苦的事莫过于此。如果上天能够给我再来一次的机会,我一定对它说‘臭傻嗨’。如果一定要给这三个字加上期限的话,我希望是app被拒之后。”Runtime感慨万千的说了这些骚话,我瞬间精神抖擞,听它讲诉它与消息之间的八卦。
向一个对象发送一个它不能处理的消息是错误的,但是,在错误发生之前,Runtime系统会给此对象第二次处理这个消息的机会。
- 重写
forwardingTargetForSelector:
方法。返回值可以是一个能够处理此消息的对象,或者是nil。 - 重写
forwardInvocation:
方法。参数NSInvocation对象包含了消息的接收者、选择器、参数和返回值。
注: 在内存管理方面,这个类不保留默认包含的调用的参数,我们可以使用retainArguments
方法破招。
重写forwardingTargetForSelector:
方法并返回一个对象,这样做可以模拟出多重继承的效果。这里着重说一下转发机制与多重继承的重要区别:多重继承在一个对象中组合了不同的能力,它倾向于大的,多方面的对象;转发机制是给不同的对象分配不同的职责,它将问题分解成更小的对象,并把这些对象以一种透明的方式与消息发送者关联。
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([otherObject respondsToSelector:aSelector]) {
return otherObject;
}
return nil; //return [super forwardingTargetForSelector:aSelector];
}
如果希望对象能够像正常继承那样正确返回,你就需要重新实现 respondsToSelector:
方法,例如:
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ( [super respondsToSelector:aSelector] )
return YES;
else {
/* 如果消息能够通过转发机制完成, 就返回YES */
}
return NO;
}
重写forwardInvocation:
方法的同时,也必须重写methodSignatureForSelector:
方法,因为需要将方法的描述(签名)告知响应消息的代理对象。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
// 这里的invocation就是通过返回的signature初始化来的
- (void)forwardInvocation:(NSInvocation *)invocation
{
SEL aSelector = [invocation selector];
if ([friend respondsToSelector:aSelector])
[invocation invokeWithTarget:friend];
else
[super forwardInvocation:invocation];
}
一包辣条吃完,Runtime就给我透漏了这么多招式,我顿时感慨万千:这要是靠套话的方式去了解它们,我得花多少钱买酒啊。
关注微信公众号CodingArtist,可以第一时间得到文章更新通知! _