/**此的方法是當(dāng)你調(diào)用一個(gè)不存在的類(lèi)方法的時(shí)候,會(huì)調(diào)用這個(gè)方法,默認(rèn)返回NO,你可以加上自己的處理然后返回YES。 */+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);/**此方法和上面的方法一樣,只是處理的是對(duì)象方法。 */+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);/**此方法是將你調(diào)用的不存在的方法重定向到一個(gè)其他聲明了這個(gè)方法的類(lèi),只需要你返回一個(gè)有這個(gè)方法的target。 */- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);/**此方法是將你調(diào)用的不存在的方法打包成NSInvocation傳給你。做完你自己的處理后,調(diào)用invokeWithTarget:方法讓某個(gè)target觸發(fā)這個(gè)方法。 */- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");二、方法攔截添加運(yùn)用
首先我們創(chuàng)建一個(gè)測(cè)試類(lèi)TestClass,然后后調(diào)用一個(gè)該類(lèi)里面沒(méi)有的對(duì)象方法 _testClass = [TestClass new]; [_testClass performSelector:@selector(testMethod:) withObject:@"test"]; 正常情況下程序會(huì)崩潰,因?yàn)樵擃?lèi)沒(méi)有此方法,父類(lèi)也沒(méi)有,我們也沒(méi)有做相應(yīng)處理,日志如下2017-02-07 10:53:42.318 RuntimeDemo[1809:61956] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[TestClass testMethod:]: unrecognized selector sent to instance 0x6080000021d0' 如果我們重寫(xiě)resolveInstanceMethod并在方法里面增加該方法,那面程序就會(huì)運(yùn)行通過(guò),void runAddMethod(id self, SEL _cmd, NSString *string){ NSLog(@"add C IMP %@", string);}+ (BOOL)resolveInstanceMethod:(SEL)sel{ //給本類(lèi)動(dòng)態(tài)添加一個(gè)方法 if ([NSStringFromSelector(sel) isEqualToString:@"testMethod:"]) { class_addMethod(self, sel, (IMP)runAddMethod, "v@:*"); } return YES;}其中class_addMethod的四個(gè)參數(shù)分別是:
Class cls 給哪個(gè)類(lèi)添加方法,本例中是selfSEL name 添加的方法,本例中是重寫(xiě)的攔截調(diào)用傳進(jìn)來(lái)的selector。IMP imp 方法的實(shí)現(xiàn),C方法的方法實(shí)現(xiàn)可以直接獲得。如果是OC方法,可以用+ (IMP)instanceMethodForSelector:(SEL)aSelector;獲得方法的實(shí)現(xiàn)。"v@:*"方法的簽名,代表有一個(gè)參數(shù)的方法。
三、關(guān)聯(lián)對(duì)象
假如正在使用一個(gè)系統(tǒng)的類(lèi)并不能滿足你的需求,你需要額外添加一個(gè)屬性。這種情況的一般解決辦法就是繼承。但是,只增加一個(gè)屬性,就去繼承一個(gè)類(lèi),總是覺(jué)得太麻煩類(lèi)。這個(gè)時(shí)候,runtime的關(guān)聯(lián)屬性就發(fā)揮它的作用了。//首先定義一個(gè)全局變量,用它的地址作為關(guān)聯(lián)對(duì)象的keystatic char associatedObjectKey;//設(shè)置關(guān)聯(lián)對(duì)象objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串屬性", OBJC_ASSOCIATION_RETAIN_NONATOMIC); //獲取關(guān)聯(lián)對(duì)象NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);NSLog(@"AssociatedObject = %@", string);objc_setAssociatedObject的四個(gè)參數(shù):id object給誰(shuí)設(shè)置關(guān)聯(lián)對(duì)象。const void *key關(guān)聯(lián)對(duì)象唯一的key,獲取時(shí)會(huì)用到。id value關(guān)聯(lián)對(duì)象。objc_AssociationPolicy關(guān)聯(lián)策略,有以下幾種策略:enum { OBJC_ASSOCIATION_ASSIGN = 0, OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, OBJC_ASSOCIATION_COPY_NONATOMIC = 3, OBJC_ASSOCIATION_RETAIN = 01401, OBJC_ASSOCIATION_COPY = 01403};如果你熟悉OC,看名字應(yīng)該知道這幾種策略就是屬性修飾關(guān)鍵字。objc_getAssociatedObject的兩個(gè)參數(shù)。id object獲取誰(shuí)的關(guān)聯(lián)對(duì)象。const void *key根據(jù)這個(gè)唯一的key獲取關(guān)聯(lián)對(duì)象。其實(shí),你還可以把添加和獲取關(guān)聯(lián)對(duì)象的方法寫(xiě)在你需要用到這個(gè)功能的類(lèi)的類(lèi)別中,方便使用。//添加關(guān)聯(lián)對(duì)象- (void)addAssociatedObject:(id)object{ objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}//獲取關(guān)聯(lián)對(duì)象- (id)getAssociatedObject{ return objc_getAssociatedObject(self, _cmd);}注意:這里面我們把getAssociatedObject方法的地址作為唯一的key,_cmd代表當(dāng)前調(diào)用方法的地址。三、方法交換
方法交換,顧名思義,就是將兩個(gè)方法的實(shí)現(xiàn)交換。例如,將A方法和B方法交換,調(diào)用A方法的時(shí)候,就會(huì)執(zhí)行B方法中的代碼,反之亦然。#import "UIViewController+swizzling.h"#import <objc/runtime.h>@implementation UIViewController (swizzling)//load方法會(huì)在類(lèi)第一次加載的時(shí)候被調(diào)用//調(diào)用的時(shí)間比較靠前,適合在這個(gè)方法里做方法交換+ (void)load{ //方法交換應(yīng)該被保證,在程序中只會(huì)執(zhí)行一次 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //獲得viewController的生命周期方法的selector SEL systemSel = @selector(viewWillAppear:); //自己實(shí)現(xiàn)的將要被交換的方法的selector SEL swizzSel = @selector(swiz_viewWillAppear:); //兩個(gè)方法的Method Method systemMethod = class_getInstanceMethod([self class], systemSel); Method swizzMethod = class_getInstanceMethod([self class], swizzSel); //首先動(dòng)態(tài)添加方法,實(shí)現(xiàn)是被交換的方法,返回值表示添加成功還是失敗 BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod)); if (isAdd) { //如果成功,說(shuō)明類(lèi)中不存在這個(gè)方法的實(shí)現(xiàn) //將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn) class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod)); }else{ //否則,交換兩個(gè)方法的實(shí)現(xiàn) method_exchangeImplementations(systemMethod, swizzMethod); } });}- (void)swiz_viewWillAppear:(BOOL)animated{ //這時(shí)候調(diào)用自己,看起來(lái)像是死循環(huán) //但是其實(shí)自己的實(shí)現(xiàn)已經(jīng)被替換了 [self swiz_viewWillAppear:animated]; NSLog(@"swizzle");}@end在一個(gè)自己定義的viewController中重寫(xiě)viewWillAppear- (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; NSLog(@"viewWillAppear");}
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注