? ? ? ? 在iOS中調(diào)用一個方法,基本上有三種調(diào)用方式:①直接用對應類調(diào)用對應的類方法改艇,類對象調(diào)用對象方法霍殴。②使用performSelector的方式調(diào)用。③使用NSInvocation去調(diào)用方法拼苍。
一、直接用對應類調(diào)用對應的類方法调缨,類對象調(diào)用對象方法疮鲫。
? ? ? ? 這是iOS中最經(jīng)常使用的方法調(diào)用方式,即消息機制弦叶。
? ? ? ? 舉例: 自定義類 SomeObject
?[SomeObject? Test];? // 類方法的調(diào)用
SomeObject *someOb = [[SomeObject alloc] init];?
?[someOb test]; ?// 對象方法的調(diào)用
? ? ? ? 這種調(diào)用方式外界在調(diào)用時必須要引入對應的類俊犯,其方法必須在該類的.h文件中聲明。
二伤哺、使用performSelector的方式調(diào)用燕侠。
[delegate imageDownloader:self didFinishWithImage:image];
[delegate performSelector:@selector(imageDownloader:didFinishWithImage:) ? ?withObject:self ?withObject:image]者祖;
1、performSelector是運行時系統(tǒng)負責去找方法的贬循,在編譯時候不做任何校驗咸包;如果直接調(diào)用編譯是會自動校驗。如果imageDownloader:didFinishWithImage:image:不存在杖虾,那么直接調(diào)用在編譯時候就能夠發(fā)現(xiàn)(借助Xcode可以寫完就發(fā)現(xiàn))烂瘫,但是使用performSelector的話一定是在運行時候才能發(fā)現(xiàn)(此時程序崩潰);Cocoa支持在運行時向某個類添加方法奇适,即方法編譯時不存在坟比,但是運行時候存在,這時候必然需要使用performSelector去調(diào)用嚷往。所以有時候如果使用了performSelector葛账,為了程序的健壯性,會使用檢查方法
- (BOOL)respondsToSelector:(SEL)aSelector;
2皮仁、直接調(diào)用方法時候籍琳,一定要在頭文件中聲明該方法的使用,也要將頭文件import進來贷祈。而使用performSelector時候趋急,可以不用import頭文件包含方法的對象,直接用performSelector調(diào)用即可势誊。
三呜达、使用NSInvocation去調(diào)用方法
? ? ? ? 第二種方式調(diào)用方法其作用很相似與函數(shù)指針,通過performSelector:withObject:函數(shù)可以直接調(diào)用這個消息粟耻。但是perform相關的這些函數(shù)查近,有一個局限性,其參數(shù)數(shù)量不能超過2個挤忙,否則要做很麻煩的處理霜威,與之相對,NSInvocation也是一種消息調(diào)用的方法册烈,并且它的參數(shù)沒有限制戈泼。
一、初始化與調(diào)用
在官方文檔中有明確說明茄厘,NSInvocation對象只能使用其類方法來初始化矮冬,不可使用alloc/init方法谈宛。它執(zhí)行調(diào)用之前次哈,需要設置兩個方法:setSelector: 和setArgument:atIndex:
- (void)viewDidLoad {
? ? [super viewDidLoad];
? ? //創(chuàng)建一個函數(shù)簽名,這個簽名可以是任意的,但需要注意吆录,簽名函數(shù)的參數(shù)數(shù)量要和調(diào)用的一 致窑滞。?
? ? SEL myMethod =@selector(myLog);
? ? //通過簽名初始化
? ? NSMethodSignature* sig? = [NSNumber instanceMethodSignatureForSelector:@selector(init)];
? ? NSInvocation* invocatin = [NSInvocation invocationWithMethodSignature:sig];
? ? //設置target
? ? [invocatin setTarget:self];
? ?//設置selecteor
? ?[invocatin setSelector:myMethod];
? //消息調(diào)用
? [invocatin invoke];
}
-(void)myLog{
? ?NSLog(@"MyLog");
}
注意:簽名函數(shù)的參數(shù)數(shù)量要和調(diào)用函數(shù)的一致。測試后發(fā)現(xiàn),當簽名函數(shù)參數(shù)數(shù)量大于被調(diào)函數(shù)時哀卫,也是沒有問題的巨坊。
調(diào)用多參數(shù)的方法,我們可以這樣寫:
-?(void)viewDidLoad?{????
? ? [superviewDidLoad];????
? ? SEL?myMethod?=@selector(myLog:parm:parm:);
? ? ?NSMethodSignature*?sig??=?[[selfclass]?instanceMethodSignatureForSelector:myMethod];
? ? ?NSInvocation*?invocatin?=?[NSInvocationinvocationWithMethodSignature:sig];????
? ? ?[invocatin?setTarget:self];????
? ? ?[invocatin?setSelector:myMethod2];
? ? ?inta=1;intb=2;intc=3;????
? ? ?[invocatin?setArgument:&a?atIndex:2];????
? ? [invocatin?setArgument:&b?atIndex:3];????
? ? [invocatin?setArgument:&c?atIndex:4];
? ? [invocatin?invoke];
}
-(void)myLog:(int)a?parm:(int)b?parm:(int)c{
? ? NSLog(@"MyLog%d:%d:%d",a,b,c);
}
注意:1此改、這里設置參數(shù)的Index 需要從2開始趾撵,因為前兩個被selector和target占用。下面這樣寫也沒有任何問題:
-?(void)viewDidLoad?{? ?
? ? [superviewDidLoad];
? ? SEL?myMethod?=@selector(myLog:parm:parm:);? ? SEL?myMethod2?=@selector(myLog);
? ? NSMethodSignature*?sig??=?[[selfclass]?instanceMethodSignatureForSelector:myMethod];
? ? NSInvocation*?invocatin?=?[NSInvocationinvocationWithMethodSignature:sig];
? ? ViewController?*?view?=self;
? ? [invocatin?setArgument:&view?atIndex:0];
? ? [invocatin?setArgument:&myMethod2?atIndex:1];
? ? inta=1;intb=2;intc=3;
? ? [invocatin?setArgument:&a?atIndex:2];?
? ? [invocatin?setArgument:&b?atIndex:3];
? ? [invocatin?setArgument:&c?atIndex:4];
? ? [invocatin?retainArguments];
? ? [invocatin?invoke];}
-(void)myLog:(int)a?parm:(int)b?parm:(int)c{
? ? NSLog(@"MyLog%d:%d:%d",a,b,c);
}
2共啃、這里的傳參方式必須是傳遞參數(shù)地址占调。
二、NSInvocation的返回值
NSInvocation對象移剪,是可以有返回值的究珊,然而這個返回值,并不是其所調(diào)用函數(shù)的返回值纵苛,需要我們手動設置:
-?(void)viewDidLoad?{
? ? [superviewDidLoad];
? ? SEL?myMethod?=@selector(myLog:parm:parm:);
? ? NSMethodSignature*?sig??=?[[selfclass]?instanceMethodSignatureForSelector:myMethod];
? ?NSInvocation*?invocatin?=?[NSInvocationinvocationWithMethodSignature:sig];
? ? [invocatin?setTarget:self];
? ? [invocatin?setSelector:myMethod2];
? ? ViewController?*?view?=self;
? ? inta=1;intb=2;intc=3;
? ? [invocatin?setArgument:&view?atIndex:0];
? ? [invocatin?setArgument:&myMethod2?atIndex:1];
? ? [invocatin?setArgument:&a?atIndex:2];
? ? [invocatin?setArgument:&b?atIndex:3];
? ? [invocatin?setArgument:&c?atIndex:4];
? ? [invocatin?retainArguments];//我們將c的值設置為返回值
? ?[invocatin?setReturnValue:&c];
? ?int d;//取這個返回值
? ? [invocatin?getReturnValue:&d];
? NSLog(@"%d",d);
}
-(int)myLog:(int)a?parm:(int)b?parm:(int)c{
? ? NSLog(@"MyLog%d:%d:%d",a,b,c);
? ? returna+b+c;
}
注意:這里的操作傳遞的都是地址剿涮。如果是OC對象,也是取地址攻人。
三取试、關于內(nèi)存
可以注意到- (void)retainArguments;這個方法,它會將傳入的所有參數(shù)以及target都retain一遍贝椿。
總結:
平時開發(fā)中基本很少使用后邊兩張方式想括,但是假如想要修改一些系統(tǒng)方法的私有屬性,或者調(diào)用私有方法時直接調(diào)用顯然是無法滿足需求的此時就需要使用上邊的方式調(diào)用了烙博。第二種方式相較于第三種方式更加簡單一些瑟蜈,但是其局限性就是無法調(diào)用太多參數(shù)的方法,或者參數(shù)是非object對象的參數(shù)渣窜。第三類方法基本可以實現(xiàn)所有方法調(diào)用铺根,缺點就是比較繁瑣。
使用第三類方法解決問題的實際案例
Hacky way 解決 iOS 7 UISearchBar 默認文本居左展示問題