ios runtime部分事例方法說明

技術 科技優家 2017-04-16

一.場景--動態改變變量

unsigned int count = 0;
    Ivar *ivar = class_copyIvarList([self.person class], &count);
    for (int i = 0; i<count; i++) {
        Ivar var = ivar[i];
        const char *varName = ivar_getName(var);
        NSString *proname = [NSString stringWithUTF8String:varName];
        
        if ([proname isEqualToString:@"_name"]) {   //這裡別忘了給屬性加下劃線
 object_setIvar(self.person, var, @"daming");
 break;
        }
    }
    NSLog(@"XiaoMing change name  is %@",self.person.name);
    self.textfield.text = self.self.person.name;

方法說明:

1.Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

用法:返回類的所有屬性和變量

參數:class 類型

outCount 類型的數目

返回:Ivar 指針 當做組來用

拓展:class_copyPropertyList 返回對象類的屬性(@property申明的屬性)

2.const char *ivar_getName(Ivar v)

用法: 返回實例變量的名稱。

拓展:ivar_getOffset: 返回實例變量的偏移量。

ivar_getTypeEncoding:返回實例變量的類型字符串。

3. void object_setIvar(id obj, Ivar ivar, id value)

用法:修改類型的變量

參數:obj 所選類

ivar 實例變量

value 更改變量

拓展:id object_getIvar(id obj, Ivar ivar) 獲取類實例的變量

二、場景--添加方法

class_addMethod([self.person class], @selector(guess), (IMP)guessAnswer, "v@:");
    if ([self.person respondsToSelector:@selector(guess)]) {
        //Method method = class_getInstanceMethod([self.xiaoMing class], @selector(guess));
        [self.person performSelector:@selector(guess)];
        
    } else{
        NSLog(@"Sorry,I don't know");
    }
    self.textview.text = @"beijing";

1.BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) //Adds a new method to a class with a given name and implementation.

用法:為類添加一個方法

參數:class 類

SEL 方法選擇器 相當於名字

IMP 方法指針

types 描述方法參數類型的字符數組

說明:sel與方法指針名字可以不同 一個是名字 一個是指針地址

三、場景--動態方法交換

    self.person = [Person new];
    
    NSLog(@"%@",_person.sayName);
    
    NSLog(@"%@",_person.saySex);
    
    Method m1 = class_getInstanceMethod([self.person class], @selector(sayName));
    Method m2 = class_getInstanceMethod([self.person class], @selector(saySex));
    
    method_exchangeImplementations(m1, m2);

1.Method class_getInstanceMethod(Class cls, SEL name) 用法:獲取某類的方法(Method類)

參數:class 類

SEL 方法選擇器

2.void method_exchangeImplementations(Method m1, Method m2) 用法:交換方法

參數:Method 方法

四、場景--替換方法

//這裡也可以使用 [self.person class],不過要先初始化
    Method m1 = class_getInstanceMethod([Person class], @selector(sayName));
    Method m2 = class_getInstanceMethod([Tool class], @selector(changeMethod));
    
    method_exchangeImplementations(m1, m2);

用法說過了,嗯!

五:場景--方法上增加額外功能

+ (void)load {
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        Class selfClass = [self class];
        
        SEL oriSEL = @selector(sendAction:to:forEvent:);
        Method oriMethod = class_getInstanceMethod(selfClass, oriSEL);
        
        SEL cusSEL = @selector(mySendAction:to:forEvent:);
        Method cusMethod = class_getInstanceMethod(selfClass, cusSEL);
        
        BOOL addSucc = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
        if (addSucc) {
 class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
        }else {
 method_exchangeImplementations(oriMethod, cusMethod);
        }
        
    });
}
- (void)mySendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {
[[Tool sharedManager] addCount];
[self mySendAction:action to:target forEvent:event];
}

說明:在程序運行時,Runtime會將所有的Class和Category加載到內存中,這時,會調用類的load方法,通知我們Class或Category已經被加載到內存中。

代碼邏輯:交換了方法 ,執行邏輯就改變了 :mySendAction觸發->再次調用轉到原有sendAction方法

1. IMP method_getImplementation(Method m)

用法:獲取IMP

拓展:const char *method_getTypeEncoding(Method m) 獲取說明

SEL method_getName(Method m) 獲取name(SEL)

2.IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

用法:Replaces the implementation of a method for a given class.

注意:註銷 free(ivars);

參考資料來源:https://github.com/Tuccuay/RuntimeSummary