Objective-C runtime運(yùn)行時(shí)詳解

最近看了一下runtime運(yùn)行時(shí)方面的文章,總結(jié)一下,加上自己的一下理解,如果文章有未全和不足的地方,歡迎各位在下方留言補(bǔ)充和指正。

image.png

目錄:

  1. runtime 消息機(jī)制
  2. runtime 添加屬性
  3. runtime 交換方法
  4. runtime 動(dòng)態(tài)添加方法
  5. runtime Class的常見(jiàn)方法

1.獲取成員變量列表
2.獲取屬性列表
3.獲取方法列表
4.獲取協(xié)議列表
5.獲得類(lèi)方法
6.獲得實(shí)例方法
7.添加方法
8.替換原方法實(shí)現(xiàn)
9.交換兩個(gè)方法

  1. runtime method swizzling 黑魔法

runtime 消息機(jī)制

對(duì)于OC代碼,調(diào)用方法的實(shí)質(zhì)就是一個(gè)消息發(fā)送,OC底層通過(guò)runtime實(shí)現(xiàn)
消息機(jī)制原理:對(duì)象根據(jù)方法編號(hào)SEL去映射表查找對(duì)應(yīng)的方法實(shí)現(xiàn)。
每一個(gè) OC 的方法,底層必然有一個(gè)與之對(duì)應(yīng)的 runtime 方法。

  • 首先必須要導(dǎo)入頭文件 #import <objc/message.h>

  • 在使用objc_msgSend方法編譯時(shí)可能出現(xiàn)報(bào)錯(cuò)的情況,對(duì)應(yīng)的解決辦法如下
    image.png
  • 新建一個(gè)Person類(lèi) Person.h中代碼

/** 實(shí)例方法*/
// 無(wú)參數(shù)無(wú)返回值
- (void)run1;
// 無(wú)參數(shù)有返回值
- (NSString *)run2;
// 有參數(shù)無(wú)返回值
- (void)run3:(NSString *)string;
// 有參數(shù)有返回值
- (NSString *)run4:(NSString *)string;

/** 類(lèi)方法*/
// 無(wú)參數(shù)無(wú)返回值
+ (void)run1;
// 無(wú)參數(shù)有返回值
+ (NSString *)run2;
// 有參數(shù)無(wú)返回值
+ (void)run3:(NSString *)string;
// 有參數(shù)有返回值
+ (NSString *)run4:(NSString *)string;
  • Person.m中代碼
- (void)run1 {
    NSLog(@"實(shí)例方法 :run1");
}

- (NSString *)run2 {
    return @"實(shí)例方法 :run2";
}

- (void)run3:(NSString *)string {
    NSLog(@"run3 --- 參數(shù):%@", string);
}

- (NSString *)run4:(NSString *)string {
    return [NSString stringWithFormat:@"run4 --- 參數(shù):%@", string];
}

+ (void)run1 {
    NSLog(@"run1 classMethod");
}

+ (NSString *)run2 {
    return @"run2 classMethod";
}

+ (void)run3:(NSString *)string {
    NSLog(@"run3 classMethod --- 參數(shù):%@", string);
}

+ (NSString *)run4:(NSString *)string {
    return [NSString stringWithFormat:@"run4 classMethod --- 參數(shù):%@", string];
}
  • 利用objc_msgSend調(diào)用上面的這些方法
/** 對(duì)象方法/實(shí)例方法 */
    // 底層的實(shí)際寫(xiě)法
    Person *person = objc_msgSend(objc_getClass("Person"), sel_registerName("alloc"));
    person = objc_msgSend(person, sel_registerName("init"));

    NSLog(@"無(wú)參數(shù)無(wú)返回值:");
    
    ((void (*) (id, SEL)) (void *)objc_msgSend)(person, sel_registerName("run1")); 
    
    objc_msgSend(person, @selector(run1));
    
    NSLog(@"無(wú)參數(shù)有返回值:");
    
    NSString *run2Return1 = ((NSString *(*) (id, SEL)) (void *)objc_msgSend)(person, sel_registerName("run2"));
    NSLog(@"%@", run2Return1);
    
    NSString *run2Return2 = objc_msgSend(person, @selector(run2));
    NSLog(@"%@", run2Return2);
    
    NSLog(@"有參數(shù)無(wú)返回值:");
    
    ((void (*) (id, SEL, NSString *)) (void *)objc_msgSend)(person, sel_registerName("run3:"), @"3333");

    
    NSLog(@"有參數(shù)有反回值:");
    
    NSString *run4Return1 = ((NSString *(*) (id, SEL, NSString *)) (void *)objc_msgSend)(person, sel_registerName("run4:"), @"4444");
    NSLog(@"%@", run4Return1);
    
    NSLog(@"--------------------------");
    
    /** 類(lèi)方法 */
    
    NSLog(@"classMethod - 無(wú)參數(shù)無(wú)返回值:");
    
    ((void (*) (id, SEL)) (void *)objc_msgSend)(Person.class, sel_registerName("run1"));
    
    objc_msgSend(Person.class, @selector(run1));
    
    NSLog(@"classMethod - 無(wú)參數(shù)有返回值:");
    
    NSString *run2Return1Class = ((NSString *(*) (id, SEL)) (void *)objc_msgSend)(Person.class, sel_registerName("run2"));
    NSLog(@"%@", run2Return1Class);
    
    NSString *run2Return2Class = objc_msgSend(Person.class, @selector(run2));
    NSLog(@"%@", run2Return2Class);
    
    NSLog(@"classMethod - 有參數(shù)無(wú)返回值:");
    
    ((void (*) (id, SEL, NSString *)) (void *)objc_msgSend)(Person.class, sel_registerName("run3:"), @"3333");
    
    NSLog(@"classMethod - 有參數(shù)有反回值:");
    
    NSString *run4Return1Class = ((NSString *(*) (id, SEL, NSString *)) (void *)objc_msgSend)(Person.class, sel_registerName("run4:"), @"4444");
    NSLog(@"%@", run4Return1Class);
  • 注:
/**
   錯(cuò)誤寫(xiě)法(arm64崩潰偶爾發(fā)生)
   */
    objc_msgSend(person, sel_registerName("run3:"), @"12345678");
    /**
     標(biāo)準(zhǔn)寫(xiě)法
     */
    ((void (*) (id, SEL, NSString *)) (void *)objc_msgSend)(person, sel_registerName("run3:"), @"不能直接寫(xiě)objc_msgSend,會(huì)出現(xiàn)崩潰的現(xiàn)象(正常應(yīng)該是可以的)");
  • 解釋一下標(biāo)準(zhǔn)寫(xiě)法前面參數(shù)
((void (*) (id, SEL)) (void *)objc_msgSend)
1、第一個(gè)void代表是否有返回值
   如果返回值是NSString類(lèi)型的 例:無(wú)參數(shù)有返回值的方法
   寫(xiě)法:((NSString *(*) (id, SEL)) (void *)objc_msgSend)
2、(id, SEL)
   如果方法有參數(shù),參數(shù)類(lèi)型是NSString 例:有參數(shù)無(wú)返回值
   寫(xiě)法:((void *(*) (id, SEL, NSString *)) (void *)objc_msgSend)
3、關(guān)于離objc_msgSend最近的void從互聯(lián)網(wǎng)上還沒(méi)找到具體含義,還望知道的好友留言或私信告知
應(yīng)用與注意

注:使用objc_msgSend()創(chuàng)建對(duì)象不能自動(dòng)釋放,對(duì)象需要手動(dòng)release。使用runtime執(zhí)行初始化方法創(chuàng)建的對(duì)象的時(shí)候是不在ARC控制之下的,所以在該類(lèi)銷(xiāo)毀的時(shí)候需要手動(dòng)release
應(yīng)用:使用objc_msgSend()創(chuàng)建對(duì)象時(shí)好處,應(yīng)用之一就是在一個(gè)控制器要跳轉(zhuǎn)多個(gè)控制器的時(shí)候,不再需要每個(gè)控制器都單獨(dú)寫(xiě)一遍初始化,也不再需要每一個(gè)控制器單獨(dú)寫(xiě)release方法,使用runtime的話,使用一個(gè)或者根據(jù)情況使用幾個(gè)回調(diào),返回控制器的類(lèi)名以及相應(yīng)的參數(shù)就好了。

Class class = objc_getClass(controllerName.UTF8String); //或者 NSStringFromClass(<#Class  _Nonnull __unsafe_unretained aClass#>)
        
id viewController = ((id(*)(id,SEL))objc_msgSend)(class,NSSelectorFromString(@"new"));

 [self xpz_pushViewController:viewController];
// 導(dǎo)航控制器獲得控制權(quán)后進(jìn)行release即可
- (void)xpz_pushViewController:(__kindof UIViewController *)viewController
{
    [self pushViewController:viewController];
    
    //release
    ((void(*)(id,SEL))objc_msgSend)(viewController,NSSelectorFromString(@"release"));
}

category添加屬性

1.面試中經(jīng)常會(huì)被問(wèn)到如何給category添加屬性,在平時(shí)我們偶爾也會(huì)遇到想要在分類(lèi)中添加屬性的情況,雖然我們用了@property,但是僅僅會(huì)自動(dòng)生成get和set方法的聲明,并沒(méi)有帶下劃線的屬性和方法實(shí)現(xiàn)生成,其實(shí)可以使用runtime動(dòng)態(tài)添加屬性方法
2.還有另外一種方法: use @dynamic or provide a method implementation in this category

1.使用runtime動(dòng)態(tài)添加屬性

// objc_setAssociatedObject(將某個(gè)值跟某個(gè)對(duì)象關(guān)聯(lián)起來(lái),將某個(gè)值存儲(chǔ)到某個(gè)對(duì)象中)
// object:給哪個(gè)對(duì)象添加屬性
// key:屬性名稱(chēng)
// value:屬性值
// policy:保存策略
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
  • 需求:在UIView上添加一個(gè)播放器
  • objc_getAssociatedObject有兩個(gè)參數(shù),第一個(gè)參數(shù)為從該object中獲取關(guān)聯(lián)對(duì)象,第二個(gè)參數(shù)為想要獲取關(guān)聯(lián)對(duì)象的key;

對(duì)于第二個(gè)參數(shù)const void *key,有以下四種推薦的key值:

  1. 聲明 static char kAssociatedObjectKey;,使用 &kAssociatedObjectKey 作為 key 值;
  2. 聲明 static void *kAssociatedObjectKey = &kAssociatedObjectKey;,使用 kAssociatedObjectKey 作為key值;
  3. selector ,使用 getter 方法的名稱(chēng)作為key值;
  4. 而使用_cmd可以直接使用該@selector的名稱(chēng),即hideButton,并且能保證改名稱(chēng)不重復(fù)。(與上一種方法相同)
static char playerViewKey;  // playerView
static void *playerLayerKey = &playerLayerKey; // playerLayer
/************************************************* playerView ********************************************************/
// getter
- (UIView *)playerView {
    UIView *_playerView = objc_getAssociatedObject(self, &playerViewKey);
    if (!_playerView) {
        objc_setAssociatedObject(self, &playerViewKey, _playerView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return _playerView;
}
// setter
- (void)setPlayerView:(UIView *)playerView {
    return objc_setAssociatedObject(self, &playerViewKey, playerView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}




/************************************************* avplayer ********************************************************/
//getter
- (AVPlayer *)avPlayer {
    AVPlayer *_avPlayer = objc_getAssociatedObject(self, @selector(avPlayer));
    if (!_avPlayer) {
        objc_setAssociatedObject(self, @selector(avPlayer), _avPlayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return _avPlayer;
}
//setter
- (void)setAvPlayer:(AVPlayer *)avPlayer {
    objc_setAssociatedObject(self, @selector(avPlayer), avPlayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}



/************************************************* playerLayer ********************************************************/
- (AVPlayerLayer *)playerLayer {
    return objc_getAssociatedObject(self, playerLayerKey);
}
- (void)setPlayerLayer:(AVPlayerLayer *)playerLayer {
    objc_setAssociatedObject(self, playerLayerKey, playerLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}



/************************************************* playerItem ********************************************************/
/**
 getter方法
 */
- (AVPlayerItem *)playerItem {
    return objc_getAssociatedObject(self, _cmd);
}
/**
 setter方法
 */
- (void)setPlayerItem:(AVPlayerItem *)playerItem {
    objc_setAssociatedObject(self, @selector(playerItem), playerItem, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

擴(kuò)展:

objc_getAssociatedObject與objc_setAssociatedObject方法另一種用途,在tableViewCell中的btn點(diǎn)擊事件中使用

在- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;方法中使用:
if (self.GroupListArr.count > 0) {
            cell.hidden = NO;
            CB_ActivityGroupModel *model = self.GroupListArr[0];
            [cell setModel:model];
            [cell.joinGroupBtn addTarget:self action:@selector(joinGroupAction:) forControlEvents:(UIControlEventTouchUpInside)];
            objc_setAssociatedObject(cell.joinGroupBtn, &joinGroup, model, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
使用
- (void)joinGroupAction:(UIButton *)btn
{
    CB_ActivityGroupModel *model = objc_getAssociatedObject(btn, &joinGroup);
    CB_ActivityGroupDetailsVC *vc = [[CB_ActivityGroupDetailsVC alloc]init];
    vc.goodModel = self.detailsModel;
    vc.model = model;
    [self.navigationController pushViewController:vc animated:YES];
}

runtime 交換方法

當(dāng)?shù)谌娇蚣?或者 系統(tǒng)原生方法功能不能滿(mǎn)足我們的時(shí)候,我們可以在保持系統(tǒng)原有方法功能的基礎(chǔ)上,添加額外的功能。

class_getInstanceMethod 得到類(lèi)的實(shí)例方法
class_getClassMethod 得到類(lèi)的類(lèi)方法

  • 給系統(tǒng)的imageNamed添加額外功能
#import "UIImage+Image.h"
#import <objc/runtime.h>

@implementation UIImage (Image)
+ (void)load
{
    Class class = [self class];
    // 類(lèi)方法
    // 1.獲取 imageNamed方法地址
    Method originalMethod = class_getClassMethod(class, sel_registerName("imageNamed:"));
    // 2.獲取 xpz_imageNamed方法地址
    Method swizzledMethod = class_getClassMethod(class, @selector(xpz_imageNamed:));
    // 交換 imageNamed:
    method_exchangeImplementations(originalMethod, swizzledMethod);
    
}
/**
 看清楚下面是不會(huì)有死循環(huán)的
 調(diào)用 imageNamed => xpz_imageNamed
 調(diào)用 xpz_imageNamed => imageNamed
 */
+ (nullable UIImage *)xpz_imageNamed:(NSString *)name{
    UIImage *xpz_image = [UIImage xpz_imageNamed:name];
    if (xpz_image) {
        NSLog(@"runtime添加額外功能--加載成功");
    } else {
        NSLog(@"runtime添加額外功能--加載失敗");
    }
    return xpz_image;
}
@end

/**
 不能在分類(lèi)中重寫(xiě)系統(tǒng)方法imageNamed,因?yàn)闀?huì)把系統(tǒng)的功能給覆蓋掉,而且分類(lèi)中不能調(diào)用super
 所以第二步,我們要 自己實(shí)現(xiàn)一個(gè)帶有擴(kuò)展功能的方法.
 + (UIImage *)imageNamed:(NSString *)name {
 
 }
 */
  • 給UIViewController的viewWillAppear添加額外功能,就可以再控制臺(tái)看到每次控制器的變化
#import "UIViewController+hook.h"
#import <objc/runtime.h>

@implementation UIViewController (hook)


+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(viewWillAppear:);
        SEL swizzledSelector = @selector(hook_viewWillAppear:);
        
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        // When swizzling a class method, use the following:
        // Class class = object_getClass((id)self);
        // ...
        // Method originalMethod = class_getClassMethod(class, originalSelector);
        // Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
        
        BOOL didAddMethod =
        class_addMethod(class,
                        originalSelector,
                        method_getImplementation(swizzledMethod),
                        method_getTypeEncoding(swizzledMethod));
        
        if (didAddMethod) {
            class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

#pragma mark - Method Swizzling

- (void)hook_viewWillAppear:(BOOL)animated {
    [self hook_viewWillAppear:animated];
    NSLog(@"viewWillAppear: %@", self);
}

打印

[控制器:UIViewController+hook.m -- line: 53行]  viewWillAppear: <NavigationViewController: 0x1060bfc00>
[控制器:UIViewController+hook.m -- line: 53行]  viewWillAppear: <GroupViewController: 0x105a6c960>
[控制器:UIViewController+hook.m -- line: 53行]  viewWillAppear: <NavigationViewController: 0x10607e400>
  • 為什么要添加didAddMethod判斷?

先嘗試添加原SEL其實(shí)是為了做一層保護(hù),因?yàn)槿绻@個(gè)類(lèi)沒(méi)有實(shí)現(xiàn)originalSelector,但其父類(lèi)實(shí)現(xiàn)了,那class_getInstanceMethod會(huì)返回父類(lèi)的方法。這樣method_exchangeImplementations替換的是父類(lèi)的那個(gè)方法,這當(dāng)然不是我們想要的。所以我們先嘗試添加 orginalSelector,如果已經(jīng)存在,再用 method_exchangeImplementations 把原方法的實(shí)現(xiàn)跟新的方法實(shí)現(xiàn)給交換掉。

大概的意思就是我們可以通過(guò)class_addMethod為一個(gè)類(lèi)添加方法(包括方法名稱(chēng)(SEL)和方法的實(shí)現(xiàn)(IMP)),返回值為BOOL類(lèi)型,表示方法是否成功添加。需要注意的地方是class_addMethod會(huì)添加一個(gè)覆蓋父類(lèi)的實(shí)現(xiàn),但不會(huì)取代原有類(lèi)的實(shí)現(xiàn)。也就是說(shuō)如果class_addMethod返回YES,說(shuō)明子類(lèi)中沒(méi)有方法originalSelector,通過(guò)class_addMethod為其添加了方法originalSelector,并使其實(shí)現(xiàn)(IMP)為我們想要替換的實(shí)現(xiàn)。

runtime動(dòng)態(tài)地添加方法

float runtime_addMethod(id receiver, SEL sel, const void *arg1, int arg2)
{
    NSLog(@"方法名:%s, 參數(shù)1:%@, 參數(shù)2:%d", __FUNCTION__, [NSString stringWithUTF8String:arg1], arg2);
    return 1;
}

    Person_runtimeVC *vc = objc_msgSend(objc_getClass("Person_runtimeVC"), sel_registerName("alloc"));
    vc = objc_msgSend(vc, sel_registerName("init"));
    /** 添加方法*/
    // 動(dòng)態(tài)添加run方法
    // class: 給哪個(gè)類(lèi)添加方法
    // SEL: 添加哪個(gè)方法,即添加方法的方法編號(hào)
    // IMP: 方法實(shí)現(xiàn) => 函數(shù) => 函數(shù)入口 => 函數(shù)名(添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址))
    // type: 方法類(lèi)型,(返回值+參數(shù)類(lèi)型) v:void @:對(duì)象->self :表示SEL->_cmd
    class_addMethod([vc class], NSSelectorFromString(@"runtime_addMethod"), (IMP)runtime_addMethod, "f@:r^vd");
    int returnValue = ((float (*)(id, SEL, const void *, int))objc_msgSend)((id)vc, NSSelectorFromString(@"runtime_addMethod"), "參數(shù)1", 10086);
    NSLog(@"返回值:%d", returnValue);
    NSLog(@"%s", @encode(const void *));


/**打印結(jié)果*/
[控制器:ViewController.m -- line: 108行]    方法名:runtime_addMethod, 參數(shù)1:參數(shù)1, 參數(shù)2:10086
[控制器:ViewController.m -- line: 74行] 返回值:1
[控制器:ViewController.m -- line: 75行] r^v
  • 參數(shù):"f@:r^vd"
  第1個(gè)字符:表示函數(shù)(方法)返回值類(lèi)型,這里返回值類(lèi)型是 `float` ,故為 `f`
  第2、3個(gè)字符:蘋(píng)果解釋是由于函數(shù)(方法)至少帶有兩個(gè)參數(shù)(self和_cmd)還記得之前的 (id,SEL) 么,所以第2、3個(gè)字符必須是 ‘@:’,其實(shí)我們當(dāng)做固定寫(xiě)法就好了
  第4個(gè)字符根據(jù)NSLog(@"%s", @encode(const void *));打印結(jié)果可以看出來(lái)r^v是第三個(gè)參數(shù)的類(lèi)型,第四個(gè)參數(shù)是int,所以是d

蘋(píng)果官方其他類(lèi)型對(duì)照表

runtime 常見(jiàn)方法

原著http://www.itdecent.cn/p/46dd81402f63

  • 獲取成員變量
/** 獲取類(lèi)中的所有成員變量*/
    Ivar *ivarList = class_copyIvarList([Person_runtimeVC class], &count);
    for(int i = 0; i < count; i++) {
        // 根據(jù)角標(biāo),從數(shù)組取出對(duì)應(yīng)的成員變量
        Ivar ivar = ivarList[i];
        // 獲取成員變量名字
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 處理成員變量名->字典中的key(去掉 _ ,從第一個(gè)角標(biāo)開(kāi)始截取)
        NSString *key = [ivarName substringFromIndex:1];
        NSLog(@"獲取類(lèi)成員變量:%@  ---  %@",ivarName,key);
    }
  • 獲取屬性列表
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)));
   }
  • 獲取協(xié)議列表
__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]);
   }

現(xiàn)在有一個(gè)Person類(lèi),和person創(chuàng)建的xiaoming對(duì)象,有test1和test2兩個(gè)方法

  • 獲得類(lèi)方法
Class PersonClass = object_getClass([Person class]);
SEL oriSEL = @selector(test1);
Method oriMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
  • 獲得實(shí)例方法
Class PersonClass = object_getClass([xiaoming class]);
SEL oriSEL = @selector(test2);
Method cusMethod = class_getInstanceMethod(xiaomingClass, oriSEL);
  • 添加方法
BOOL addSucc = class_addMethod(xiaomingClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
  • 替換原方法實(shí)現(xiàn)
class_replaceMethod(toolClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
  • 交換兩個(gè)方法
method_exchangeImplementations(oriMethod, cusMethod);

什么是 method swizzling(俗稱(chēng)黑魔法)

  • 簡(jiǎn)單說(shuō)就是進(jìn)行方法交換
  • 在Objective-C中調(diào)用一個(gè)方法,其實(shí)是向一個(gè)對(duì)象發(fā)送消息,查找消息的唯一依據(jù)是selector的名字。利用Objective-C的動(dòng)態(tài)特性,可以實(shí)現(xiàn)在運(yùn)行時(shí)偷換selector對(duì)應(yīng)的方法實(shí)現(xiàn),達(dá)到給方法掛鉤的目的
  • 每個(gè)類(lèi)都有一個(gè)方法列表,存放著方法的名字和方法實(shí)現(xiàn)的映射關(guān)系,selector的本質(zhì)其實(shí)就是方法名,IMP有點(diǎn)類(lèi)似函數(shù)指針,指向具體的Method實(shí)現(xiàn),通過(guò)selector就可以找到對(duì)應(yīng)的IMP


    image

selector --> 對(duì)應(yīng)的IMP

runtime知識(shí)很多,后期會(huì)慢慢補(bǔ)充,歡迎廣大簡(jiǎn)友補(bǔ)充學(xué)習(xí);剛學(xué)習(xí)使用Markdown,排版上做的不是太好。
本篇筆記部分參考自以下:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,032評(píng)論 0 9
  • 本文轉(zhuǎn)載自:http://southpeak.github.io/2014/10/25/objective-c-r...
    idiot_lin閱讀 1,014評(píng)論 0 4
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí),它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 864評(píng)論 0 4
  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門(mén)動(dòng)態(tài)語(yǔ)言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,319評(píng)論 0 7
  • 作業(yè)1:好好回想一下,小時(shí)候呆呆看過(guò)什么。挑選一個(gè)印象或一幅畫(huà)面寫(xiě)出來(lái)。 2. 嘗試隨時(shí)記錄,確定自己更適合手機(jī)記...
    米果書(shū)閱讀 215評(píng)論 3 0

友情鏈接更多精彩內(nèi)容