iOS-runtime的應用

首先導入頭文件#import <objc/runtime.h>markdown

經過runtime的一系列方法,能夠獲取類的一些信息, 包括:屬性列表,方法列表,成員變量列表,和遵循的協議列表。函數

一、獲取列表
  • 獲取屬性列表

有時候會有這樣的需求,咱們須要知道當前類中每一個屬性的名字。spa

unsigned int count;
    // 獲取列表
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        // 獲取屬性名
        const char *propertyName = property_getName(propertyList[i]);
        // 打印
        NSLog(@"property-->%@", [NSString stringWithUTF8String:propertyName]);
    }
複製代碼
  • 獲取方法列表
Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Method method = methodList[i];
        NSLog(@"method-->%@", NSStringFromSelector(method_getName(method)));
    }
複製代碼
  • 獲取成員變量列表
Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
    }
複製代碼
  • 獲取協議列表
__unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
    }
複製代碼
二、動態添加方法

核心方法:class_addMethod () 首先從外部隱式調用一個不存在的方法:指針

// 隱式調用方法
[target performSelector:@selector(resolveAdd:) withObject:@"test"];
複製代碼

而後,在target對象內部重寫攔截調用的方法,動態添加方法。code

// 重寫了攔截調用的方法,並返回YES
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    
    //給本類動態添加一個方法
    if ([NSStringFromSelector(sel) isEqualToString:@"resolveAdd:"]) {
        class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
    }
    return YES;
}
// 調用新增的方法
void runAddMethod(id self, SEL _cmd, NSString *string){
    NSLog(@"add C IMP ", string);  //withObject 參數
}
複製代碼

其中class_addMethod的四個參數分別是:orm

Class: cls 給哪一個類添加方法,本例中是self SEL name: 添加的方法,本例中是重寫的攔截調用傳進來的selector。 IMP imp: 方法的實現,C方法的方法實現能夠直接得到。若是是OC方法,能夠用+ (IMP)instanceMethodForSelector:(SEL)aSelector;得到方法的實現。 "v@:*":方法的簽名,表明有一個參數的方法。對象

三、關聯對象

如今你準備用一個系統的類,可是系統的類並不能知足你的需求,你須要額外添加一個屬性。通常解決辦法就是繼承。 可是,只增長一個屬性,就去繼承一個類,以爲太麻煩類。 這個時候,runtime的關聯屬性就發揮它的做用了。在Category中添加屬性。繼承

//首先定義一個全局變量,用它的地址做爲關聯對象的key
static char associatedObjectKey;
//設置關聯對象
objc_setAssociatedObject(target, &associatedObjectKey, @"添加的字符串屬性", OBJC_ASSOCIATION_RETAIN_NONATOMIC); //獲取關聯對象
NSString *string = objc_getAssociatedObject(target, &associatedObjectKey);
NSLog(@"AssociatedObject = %@", string);
複製代碼

objc_setAssociatedObject的四個參數:ci

id object給誰設置關聯對象。 const void *key關聯對象惟一的key,獲取時會用到。 id value關聯對象。 objc_AssociationPolicy關聯策略字符串

四、動態重寫方法

在沒有一個類的實現源碼的狀況下,想改變其中一個方法的實現,除了繼承它重寫、和藉助Category重名方法以外,還有更加靈活的方法 Method Swizzle。 在OC中調用一個方法,實際上是向一個對象發送消息,查找消息的惟一依據是selector的名字。 利用OC的動態特性,能夠實如今運行時偷換selector對應的方法實現。

Method Swizzle 指的是,改變一個已存在的選擇器對應的實現過程。OC中方法的調用可以在運行時,改變類的調度表中選擇器到最終函數間的映射關係。

每一個類都有一個方法列表,存放着selector的名字及其方法實現的映射關係。IMP有點相似函數指針,指向具體的方法實現。 利用 method_exchangeImplementations 來交換2個方法中的IMP。 利用 class_replaceMethod 來修改類。 利用 method_setImplementation 來直接設置某個方法的IMP。

歸根結底,都是偷換了selector的IMP

五、方法替換

新建分類

#import <objc/runtime.h>


+ (void)load {
    // 方法交換應該被保證,在程序中只會執行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 得到須要替換的系統方法
        SEL systemSel = @selector(didDisplay);
        // 本身實現的將要被交換的方法
        SEL swizzSel = @selector(myDidDisplay);
        
        //兩個方法的Method
        Method systemMethod = class_getInstanceMethod([self class], systemSel);
        Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
        
        //首先動態添加方法,實現是被交換的方法,返回值表示添加成功仍是失敗
        BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
        if (isAdd) {
            //若是成功,說明類中不存在這個方法的實現
            //將被交換方法的實現替換到這個並不存在的實現
            class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
        } else {
            //不然,交換兩個方法的實現
            method_exchangeImplementations(systemMethod, swizzMethod);
        }
        
    });
}

-(void)myDidDisplay{
  //......do
}
複製代碼
相關文章
相關標籤/搜索