我們這一章分為三個部分:1. Runtime動態添加實現方法 2. Runtime動態方法傳遞 3. Runtime更換方法; 核心都是 “消息動態解析”。這一篇也叫淘氣篇
第一部分:“動態添加實現方法”: 只寫實例方法,但是不提供方法的實現。驗證當找不到方法的實現時,動態添加方法
創建Boby類
// Boby.h
@interface Boby : NSObject
@property (nonatomic, copy) NSString *name;
- (void)eating;
@end
- ---- > // Boby.m
#import "Boby.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation Boby
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// 動態添加方法
if ([NSStringFromSelector(sel) isEqualToString:@"eating"]) {
class_addMethod(self, sel, (IMP)eateat, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void eateat(id self, SEL cmd)
{
NSLog(@"%@ 吃貨就知道吃。。。。!。 倒架子。。。",((Boby *)self).name);
}
*----
int main(int argc, const char * argv[]) {
Boby *minBoby = [[Boby alloc] init];
minBoby.name = @"哈利波特";
[minBoby eating];
return 0;
}
看我們打印的結果吧:
`
2016-04-11 21:16:56.967 RuntimeLine[2348:195944] 哈利波特 吃貨就知道吃。。。。!。 倒架子。。。
`
> 呵呵, 。好像冤枉了,.h只是提供了個API,.m并沒有實現這個API,意思就是人家想干,卻是沒干,結果就是干了, 反正成果擺在哪兒,這是偷干,額,不對,暗度陳倉 類里面調用函數,真是騙到了,哎
人家.m沒干,沒有`eating`的方法實現,因此在調用此方法的時候,會調用`resolveInstanceMethod`方法,卻是動態添加了方法。他還可以返回No,繼續向下傳遞。接下來。。。
###第二部分:“動態方法傳遞”: 本來朕沒干,可以偷干的,卻給波特干了
下面剛才的代碼,添加一個Cat
* > 創建Cat類
// Cat.h
import <Foundation/Foundation.h>
@interface Cat : NSObject
@property (copy, nonatomic) NSString * name;
@end.h
@interface Boby : NSObject
@property (nonatomic, copy) NSString *name;
- (void)eating;
@end
* ---- > // Cat.h.m
import "Cat.h"
import "Boby.h"
@implementation Cat
// 1. 我是拒絕的 不動態添加方法, 返回NO 進入2
- (BOOL)resolveInstanceMethod:(SEL)sel
{
return NO;
}
// 2. 我再次拒絕,不指定備選對象響應aselector,進入3
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return nil;
}
// 3. 我還是拒絕,給他返回方法選擇器, 進入4 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) isEqualToString:@"eating"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 4. 算了,答應了,但是不能這么爽快,不然,修改調用對象 - (void)forwardInvocation:(NSInvocation *)anInvocation
{
Boby *boby = [[Boby alloc] init];
boby.name = @"波特哥哥";
[anInvocation invokeWithTarget:boby];
}
*----
*
> ````
//記住應該這樣的
int main(int argc, const char * argv[]) {
Cat * cat = [[Cat alloc] init];
cat.name = @"我是朕,鏟屎官。。。"; //記住應該這樣的
((void(*)(id, SEL))objc_msgSend)((id)cat, @selector(eating));
return 0;
}
//記住應該這樣的
結果打印是這樣的:
2016-04-11 21:56:07.323 RuntimeLine[2483:214357] 波特哥哥 吃貨就知道吃。。。。!。 倒架子。。。
喵喵, 。坑爹呀,. 鏟屎官,為毛沒朕的份呢???,意思就是本來朕偷干的,卻是給波特兄弟偷干了,.
其實鏟屎官使壞了,Cat(朕)在.m嬌氣,冷艷,三次拒絕之后又自己說給波特的,結果呢?鏟屎的成功更換了對象,把對象更換為波特兄弟了,朕只會跳舞不行哦, 還要會魔法才行,看來討好鏟屎官才是王道!!!
第三部分:“更換方法” 干了一件別的事
還是用Boby類來搞
// Boby.h
@interface Boby : NSObject
@end
- ---- > // Boby.m
#import "Boby.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation Boby
// 1. 我是拒絕的 不動態添加方法, 返回NO 進入2
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
return NO;
}
// 2. 我再次拒絕,不指定備選對象響應aselector,進入3
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return nil;
}
// 3. 我還是拒絕,給他返回方法選擇器, 進入4
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) isEqualToString:@"eating"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 4. 算了,答應了,但是不能這么爽快,不然,修改調用對象
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setSelector:@selector(changeMagic)];
[anInvocation invokeWithTarget:self];
}
// 如果forwardInvocation沒實現,就會調用這個方法
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
NSLog(@"消息處長不干了: %@", NSStringFromSelector(aSelector));
}
- (void)changeMagic
{
NSLog(@"變魔法。。。。");
}
*----
int main(int argc, const char * argv[]) {
Boby minBoby = [[Boby alloc] init];
((void()(id, SEL))objc_msgSend)((id)minBoby, @selector(eating));
return 0;
}
打印的結果:
`
2016-04-11 23:37:50.890 RuntimeLine[3180:255959] 變魔法。。。。
`
更換了方法,有吃變為了魔法了, 干了一件別的事。。。什么感覺
> ###案例小結:
看看我們都干了什么?!What have we done?!!!
此實戰內容是
`resolveInstanceMethod:(SEL)sel
forwardingTargetForSelector:(SEL)aSelector
methodSignatureForSelector:(SEL)aSelector
forwardInvocation:(NSInvocation *)anInvocation
doesNotRecognizeSelector:(SEL)aSelector`
這個方法使用而已,沒什么難的吧,就當學習一下scrollview這么簡單吧
回顧一下:1. 動態添加實現方法 2. 動態方法傳遞 3. 更換方法。
下一篇繼續: [《Runtime梳理(四)》](http://www.lxweimin.com/p/65dd981334d6)