1. 程式人生 > >iOS進階—Runtime基礎

iOS進階—Runtime基礎

iOS進階—目錄


GitHub參考

RunTime 基礎

一個程式的執行過程,大概就是程式碼->編譯連結->執行

C語言

#import <Foundation/Foundation.h>
void run() {
    NSLog(@"%s", __func__);
}
int main(int argc, const char * argv[]) {
    @autoreleasepool {     
        run();  
    }
    return 0;
}

執行結果:

2018-11-16 11:41:34.373347+0800 Runtime001[2263:82588] run
Program ended with exit code: 0

OC 屬於訊息傳送機制,包含SEL 和IMP,如果把OC理解為一本書,SEL (編號)代表書目錄的文章標題,IMP(指標)代表文章對應頁碼,方法實現本身則是正文

Runtime進行同類方法交換

#import <Foundation/Foundation.h>
#import "TZPerson.h"
#import "TZDog.h"
#import <objc/runtime.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        TZPerson* p = [TZPerson new];
        Method m1 = class_getInstanceMethod([p class], @selector(walk));
        Method m2 = class_getInstanceMethod([p class], @selector(run));
        IMP imp = method_getImplementation(m2);
        method_setImplementation(m1, imp);
        [p walk];   
    }
    return 0;
}

執行結果:我們呼叫的walk,執行的是run方法

2018-11-16 11:49:45.681647+0800 Runtime001[2356:87721] -[TZPerson run]
Program ended with exit code: 0

Method本身是typedef struct objc_method *Method;結構體。我們可以檢視Runtime原始碼(參考GitHub)objc-runtime-master

在這裡插入圖片描述

上面的程式碼與下面的程式碼等價

struct method_t {
    SEL name;
    const char *types;
    IMP imp; 
//    struct SortBySELAddress :
//    public std::binary_function<const method_t&,
//    const method_t&, bool>
//    {
//        bool operator() (const method_t& lhs,
//                         const method_t& rhs)
//        { return lhs.name < rhs.name; }
//    };
};
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        TZPerson* p = [TZPerson new];
        Method m1 = class_getInstanceMethod([p class], @selector(walk));
        struct method_t * m2 = class_getInstanceMethod([p class], @selector(run));
//        IMP imp = method_getImplementation(m2);
        method_setImplementation(m1, m2->imp);
        [p walk];
    }
    return 0;
}

OC在呼叫類方法時,也可以對C語言的函式進行呼叫,比如下面的例子

#import <Foundation/Foundation.h>
#import "TZPerson.h"
#import <objc/runtime.h>

void run() {
    NSLog(@"%s", __func__);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        TZPerson* p = [TZPerson new];
        Method m1 = class_getInstanceMethod([p class], @selector(walk));
        method_setImplementation(m1, (IMP)run);
        [p walk];        
    }
    return 0;
}

OC進行呼叫類交換,如下

#import <Foundation/Foundation.h>
#import "TZPerson.h"
#import "TZDog.h"
#import <objc/runtime.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        TZPerson* p = [TZPerson new];
        TZDog* d = [TZDog new];
        object_setClass(p, [d class]);
        [p walk];        
    }
    return 0;
}

執行結果如下:

2018-11-16 14:39:19.802920+0800 Runtime001[3662:134196] -[TZDog walk]
Program ended with exit code: 0

本來呼叫的Person,但是替換成了Dog類。這就是類物件傳送改變。

GitHub參考
iOS進階—目錄