Runtime的用法有哪幾種

Runtime的用法有哪幾種

1, 動態添加一個類, 就像KVO同樣, 系統是在程序運行的時候根據你要監聽的類, 動態添加一個新類繼承自該類, 而後重寫原類的setter方法並在裏面通知observer的.ide

// 建立一個類(size_t extraBytes該參數一般指定爲0, 該參數是分配給類和元類對象尾部的索引ivars的字節數。)
Class clazz = objc_allocateClassPair([NSObject class], "GoodPerson", 0);

// 添加ivar
// @encode(aType) : 返回該類型的C字符串
class_addIvar(clazz, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));

class_addIvar(clazz, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));

// 註冊該類
objc_registerClassPair(clazz);

// 建立實例對象
id object = [[clazz alloc] init];

// 設置ivar
[object setValue:@"Tracy" forKey:@"name"];

Ivar ageIvar = class_getInstanceVariable(clazz, "_age");
object_setIvar(object, ageIvar, @18);

// 打印對象的類和內存地址
NSLog(@"%@", object);

// 打印對象的屬性值
NSLog(@"name = %@, age = %@", [object valueForKey:@"name"], object_getIvar(object, ageIvar));

// 當類或者它的子類的實例還存在,則不能調用objc_disposeClassPair方法
object = nil;

// 銷燬類
objc_disposeClassPair(clazz);
--------------------- 

2,函數

經過runtime獲取一個類的全部屬性

Person *p = [[Person alloc] init];
[p setValue:@"Kobe" forKey:@"name"];
[p setValue:@18 forKey:@"age"];
//    p.address = @"廣州大學城";
p.weight = 110.0f;

// 1.打印全部ivars
unsigned int ivarCount = 0;
// 用一個字典裝ivarName和value
NSMutableDictionary *ivarDict = [NSMutableDictionary dictionary];
Ivar *ivarList = class_copyIvarList([p class], &ivarCount);
for(int i = 0; i < ivarCount; i++){
    NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivarList[i])];
    id value = [p valueForKey:ivarName];

    if (value) {
        ivarDict[ivarName] = value;
    } else {
        ivarDict[ivarName] = @"值爲nil";
    }
}
// 打印ivar
for (NSString *ivarName in ivarDict.allKeys) {
    NSLog(@"ivarName:%@, ivarValue:%@",ivarName, ivarDict[ivarName]);
}

// 2.打印全部properties
unsigned int propertyCount = 0;
// 用一個字典裝propertyName和value
NSMutableDictionary *propertyDict = [NSMutableDictionary dictionary];
objc_property_t *propertyList = class_copyPropertyList([p class], &propertyCount);
for(int j = 0; j < propertyCount; j++){
    NSString *propertyName = [NSString stringWithUTF8String:property_getName(propertyList[j])];
    id value = [p valueForKey:propertyName];

    if (value) {
        propertyDict[propertyName] = value;
    } else {
        propertyDict[propertyName] = @"值爲nil";
    }
}
// 打印property
for (NSString *propertyName in propertyDict.allKeys) {
    NSLog(@"propertyName:%@, propertyValue:%@",propertyName, propertyDict[propertyName]);
}

// 3.打印全部methods
unsigned int methodCount = 0;
// 用一個字典裝methodName和arguments
NSMutableDictionary *methodDict = [NSMutableDictionary dictionary];
Method *methodList = class_copyMethodList([p class], &methodCount);
for(int k = 0; k < methodCount; k++){
    SEL methodSel = method_getName(methodList[k]);
    NSString *methodName = [NSString stringWithUTF8String:sel_getName(methodSel)];

    unsigned int argumentNums = method_getNumberOfArguments(methodList[k]);

    methodDict[methodName] = @(argumentNums - 2); // -2的緣由是每一個方法內部都有self 和 selector 兩個參數
}
// 打印method
for (NSString *methodName in methodDict.allKeys) {
    NSLog(@"methodName:%@, argumentsCount:%@", methodName, methodDict[methodName]);
}
---------------------

3, post

動態變量控制,動態修改變量的值

-(void)changeAge{
     unsigned int count = 0;
     //動態獲取XiaoMing類中的全部屬性[固然包括私有] 
     Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count);
     //遍歷屬性找到對應age字段 
     for (int i = 0; i<count; i++) {
         Ivar var = ivar[i];
         const char *varName = ivar_getName(var);
         NSString *name = [NSString stringWithUTF8String:varName];
         if ([name isEqualToString:@"_age"]) {
             //修改對應的字段值成20
             object_setIvar(self.xiaoMing, var, @"20");
             break;
         }
     }
     NSLog(@"XiaoMing's age is %@",self.xiaoMing.age);
 }

4, code

在NSObject的分類中增長方法來避免使用KVC賦值的時候出現崩潰

在有些時候咱們須要經過KVC去修改某個類的私有變量,可是又不知道該屬性是否存在,若是類中不存在該屬性,那麼經過KVC賦值就會crash,這時也能夠經過運行時進行判斷。一樣咱們在NSObject的分類中增長以下方法。server

#import "NSObject+objc.h"
#import <objc/runtime.h>

@implementation NSObject (objc)

-(BOOL)hasProperty:(NSString *)property
{
    BOOL flag = NO;
    u_int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i++) {
        const char *propertyName = ivar_getName(ivars[i]);
        NSString *propertyString = [NSString stringWithUTF8String:propertyName];
        if ([propertyString isEqualToString:property]){
            flag = YES;
        }
    }
    return flag;
}

@end

5, 對象

利用runtime的動態交換方法實現

+(void)run
{
    NSLog(@"Person Run.....");
}

+(void)study
{
    NSLog(@"Person study.....");
}
// 獲取兩個類的類方法
    Method m1 = class_getClassMethod([Person class], @selector(run));
    Method m2 = class_getClassMethod([Person class], @selector(study));
    // 開始交換方法實現
    method_exchangeImplementations(m1, m2);
   [Person run];
    [Person study];

 

6,繼承

動態添加方法

// void(*)()
// 默認方法都有兩個隱式參數,
void eat(id self,SEL sel)
{
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
}

// 當一個對象調用未實現的方法,會調用這個方法處理,而且會把對應的方法列表傳過來.
// 恰好能夠用來判斷,未實現的方法是否是咱們想要動態添加的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{

    if (sel == @selector(eat)) {
        // 動態添加eat方法

        // 第一個參數:給哪一個類添加方法
        // 第二個參數:添加方法的方法編號
        // 第三個參數:添加方法的函數實現(函數地址)
        // 第四個參數:函數的類型,(返回值+參數類型) v:void @:對象->self :表示SEL->_cmd
        class_addMethod(self, @selector(eat), eat, "v@:");

    }

    return [super resolveInstanceMethod:sel];
}
相關文章
相關標籤/搜索