/**此的方法是當(dāng)你調(diào)用一個(gè)不存在的類方法的時(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è)方法的類,只需要你返回一個(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è)試類TestClass,然后后調(diào)用一個(gè)該類里面沒(méi)有的對(duì)象方法    _testClass = [TestClass new];    [_testClass performSelector:@selector(testMethod:) withObject:@"test"]; 正常情況下程序會(huì)崩潰,因?yàn)樵擃悰](mé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' 如果我們重寫resolveInstanceMethod并在方法里面增加該方法,那面程序就會(huì)運(yùn)行通過(guò),void runAddMethod(id self, SEL _cmd, NSString *string){    NSLog(@"add C IMP %@", string);}+ (BOOL)resolveInstanceMethod:(SEL)sel{    //給本類動(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è)類添加方法,本例中是selfSEL name 添加的方法,本例中是重寫的攔截調(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)的類并不能滿足你的需求,你需要額外添加一個(gè)屬性。這種情況的一般解決辦法就是繼承。但是,只增加一個(gè)屬性,就去繼承一個(gè)類,總是覺(jué)得太麻煩類。這個(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ì)象的方法寫在你需要用到這個(gè)功能的類的類別中,方便使用。//添加關(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ì)在類第一次加載的時(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ō)明類中不存在這個(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中重寫viewWillAppear- (void)viewWillAppear:(BOOL)animated{    [super viewWillAppear:animated];    NSLog(@"viewWillAppear");}
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注