国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

繼承自NSObject的不常用又很有用的函數(2)

2019-11-14 20:31:47
字體:
來源:轉載
供稿:網友

函數調用

Objective-C是一門動態語言,一個函數是由一個selector(SEL),和一個implement(IML)組成的。Selector相當于門牌號,而Implement才是真正的住戶(函數實現)。

和現實生活一樣,門牌可以隨便發(@selector(XXX)),但是不一定都找得到住戶,如果找不到系統會給程序幾次機會來程序正常運行,實在沒出路了才會拋出異常。下圖是objc_msgSend調用時,查找SEL的IML的過程。咱們以這個流程為例看看其中涉及的很有用的函數。

 

 

圖:運行時查找函數的流程

resolveInstanceMethod函數

原型:

+ (BOOL)resolveInstanceMethod:(SEL)name

這個函數在運行時(runtime),沒有找到SEL的IML時就會執行。這個函數是給類利用class_addMethod添加函數的機會。

根據文檔,如果實現了添加函數代碼則返回YES,未實現返回NO。

實現的例子:

//全局函數void dynamicMethodIMP(id self, SEL _cmd){    // implementation ....}@implementation MyTestObject//…//類函數+ (BOOL) resolveInstanceMethod:(SEL)aSEL{    if (aSEL == @selector(resolveThisMethodDynamically))    {          class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");          return YES;    }    return [super resolveInstanceMethod:aSel];}//@end

注意事項:

根據Demo實驗,這個函數返回的BOOL值系統實現的objc_msgSend函數并沒有參考,無論返回什么系統都會嘗試再次用SEL找IML,如果找到函數實現則執行函數。如果找不到繼續其他查找流程。

forwardingTargetForSelector:

原型:

- (id)forwardingTargetForSelector:(SEL)aSelector

流程到了這里,系統給了個將這個SEL轉給其他對象的機會

返回參數是一個對象,如果這個對象非nil、非self的話,系統會將運行的消息轉發給這個對象執行。否則,繼續查找其他流程。

實現示例:

//轉發目標類@interface NoneClass : NSObject@end@implementation NoneClass+(void)load{    NSLog(@"NoneClass _cmd: %@", NSStringFromSelector(_cmd));}- (void) noneClassMethod{    NSLog(@"_cmd: %@", NSStringFromSelector(_cmd));}@end@implementation MyTestObject////將消息轉出某對象- (id)forwardingTargetForSelector:(SEL)aSelector{    NSLog(@"MyTestObject _cmd: %@", NSStringFromSelector(_cmd));    NoneClass *none = [[NoneClass alloc] init];    if ([none respondsToSelector: aSelector]) {        return none;    }        return [super forwardingTargetForSelector: aSelector];}//@end

當執行MyTestObject對象執行[myTestObject nonClassMethod]函數時,消息會拋到NoneClass對象中執行。

 

methodSignatureForSelector:

原型:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

這個函數和后面的forwardInvocation:是最后一個尋找IML的機會。這個函數讓重載方有機會拋出一個函數的簽名,再由后面的forwardInvocation:去執行

forwardInvocation:

原型:

- (void)forwardInvocation:(NSInvocation *)anInvocation

真正執行從methodSignatureForSelector:返回的NSMethodSignature。在這個函數里可以將NSInvocation多次轉發到多個對象中,這也是這種方式靈活的地方。(forwardingTargetForSelector只能以Selector的形式轉向一個對象)

下面這個示例代碼,詮釋了這種實現優勢:

#import <Foundation/Foundation.h> @interface Book : NSObject{    NSMutableDictionary *data;}//聲明了兩個setter/getter@PRoperty (retain) NSString *title; @property (retain) NSString *author;@end @implementation Book@dynamic title, author; //不自動生成實現 - (id)init{    if ((self = [super init])) {        data = [[NSMutableDictionary alloc] init];        [data setObject:@"Tom Sawyer" forKey:@"title"];        [data setObject:@"Mark Twain" forKey:@"author"];    }    return self;} - (void)dealloc{    [data release];    [super dealloc];} - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector{    NSString *sel = NSStringFromSelector(selector);    if ([sel rangeOfString:@"set"].location == 0) {        //動態造一個 setter函數        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];    } else {        //動態造一個 getter函數        return [NSMethodSignature signatureWithObjCTypes:"@@:"];    }} - (void)forwardInvocation:(NSInvocation *)invocation{    //拿到函數名    NSString *key = NSStringFromSelector([invocation selector]);    if ([key rangeOfString:@"set"].location == 0) {        //setter函數形如 setXXX: 拆掉 set和冒號         key = [[key substringWithRange:NSMakeRange(3, [key length]-4)] lowercaseString];        NSString *obj;        //從參數列表中找到值        [invocation getArgument:&obj atIndex:2];        [data setObject:obj forKey:key];    } else {        //getter函數就相對簡單了,直接把函數名做 key就好了。        NSString *obj = [data objectForKey:key];        [invocation setReturnValue:&obj];    }} @end

doesNotRecognizeSelector:

原型:

- (void)doesNotRecognizeSelector:(SEL)aSelector

作為找不到函數實現的最后一步,NSObject實現這個函數只有一個功能,就是拋出異常。

雖然理論上可以重載這個函數實現保證不拋出異常(不調用super實現),但是蘋果文檔著重提出“一定不能讓這個函數就這么結束掉,必須拋出異常”。

使用場景

在一個函數找不到時,Objective-C提供了三種方式去補救:

1、調用resolveInstanceMethod給個機會讓添加這個實現這個函數

2、調用forwardingTargetForSelector讓別的對象去執行這個函數

3、調用methodSignatureForSelector(函數符號制造器)和forwardInvocation(函數執行器)靈活的將目標函數以其他形式執行。

如果都不中,調用doesNotRecognizeSelector拋出異常。

文章參考

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html

https://www.mikeash.com/pyblog/friday-qa-2009-03-27-objective-c-message-forwarding.html

http://blog.csdn.net/yiyaaixuexi/article/details/8970734

 

補充:respondsToSelector

原型:

+ (BOOL)respondsToSelector:(SEL)aSelector

這個函數大家再熟悉不過了,用來檢查對象是否實現了某函數。

此函數通常是不需要重載的,但是在動態實現了查找過程后,需要重載此函數讓對外接口查找動態實現函數的時候返回YES,保證對外接口的行為統一。

示例代碼(接forwardInvocation的例子):

@implementation Book//- (BOOL) respondsToSelector:(SEL)aSelector{    if (@selector(setTitle:) == aSelector ||        @selector(title) == aSelector ||        @selector(setAuthor:) == aSelector ||        @selector(author) == aSelector)    {        return YES;    }        return [super respondsToSelector: aSelector];} //@end

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 托克逊县| 华阴市| 卢湾区| 大渡口区| 宜都市| 永吉县| 泰和县| 翁牛特旗| 宝鸡市| 广宗县| 项城市| 广宁县| 连江县| 新建县| 钟山县| 东乡族自治县| 栾川县| 黎川县| 五寨县| 克拉玛依市| 新乡市| 中西区| 友谊县| 铜梁县| 古浪县| 延寿县| 汨罗市| 皋兰县| 阳高县| 抚远县| 五华县| 汶川县| 东阿县| 且末县| 河池市| 突泉县| 腾冲县| 海兴县| 澜沧| 铁力市| 土默特左旗|