筆記 - OC 轉(zhuǎn)發(fā)消息機(jī)制

消息轉(zhuǎn)發(fā)流程

目錄

  • 一、消息機(jī)制
  • 二、方法調(diào)用源碼查看
  • 三、objc_msgSend的執(zhí)行流程
  • 四、番外篇

一、簡(jiǎn)要說明OC消息機(jī)制(objc_msgSend執(zhí)行流程)

OC中的方法調(diào)用,其實(shí)都是轉(zhuǎn)換為objc_msgSend函數(shù)調(diào)用

objc_msgSend的執(zhí)行流程可以分為3大階段
- 消息發(fā)送
- 動(dòng)態(tài)方法解析
- 消息轉(zhuǎn)發(fā)

如果找不到合適的方法調(diào)用,會(huì)報(bào)錯(cuò) unrecognized selector sent to instance 0x100555ad0

二、方法調(diào)用源碼查看

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 創(chuàng)建消息接收者(receiver)
        MJPerson *person = [[MJPerson alloc] init];
        // 對(duì)象方法調(diào)用
        [person personTest];
        
        // 類方法調(diào)用
        [MJPerson initialize];
    }
    return 0;
}

和以前一樣,我們使用clang看一下源碼

$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
  • 2.1、對(duì)象方法調(diào)用 [person personTest];
// C++源碼
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personTest"));

// 刪除強(qiáng)制轉(zhuǎn)換后
objc_msgSend(person, sel_registerName("personTest"));

// sel_registerName 相當(dāng)于 @selector,所以,最終我們可以寫成
objc_msgSend(person, @selector(personTest));
  • 2.2、類方法調(diào)用[MJPerson initialize];
// C++源碼
((void (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("MJPerson"), sel_registerName("initialize"));

// 刪除強(qiáng)制轉(zhuǎn)換后
objc_msgSend(objc_getClass("MJPerson"), sel_registerName("initialize"));

// 同理,最終我們可以寫成
objc_msgSend([MJPerson class], @selector(initialize))

三、objc_msgSend的執(zhí)行流程

  • 3.1、消息發(fā)送階段
李明杰-底層原理-消息發(fā)送流程.png

objc-runtime-new.mm類中的lookUpImpOrForward方法我們可以看到其實(shí)現(xiàn)

    // Try this class's cache.
    // 從當(dāng)前類緩存中查找
    imp = cache_getImp(cls, sel);
    if (imp) goto done;


    // Try this class's method lists.
    // 從當(dāng)前類方法列表中查找
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            // 如果找到,就將它加入類的緩存中
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }


    // Try superclass caches and method lists.
    // 從父類的緩存中查找
    {
        unsigned attempts = unreasonableClassCount();
        // 一層一層向上查找(superClass->superSuperClass->...)
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Halt if there is a cycle in the superclass chain.
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }
            
            // Superclass cache.
            // 緩存父類的方法(下面順帶將父類方法緩存到自己的緩存中)
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    // 將父類的方法緩存到消息接收者里面
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Found a forward:: entry in a superclass.
                    // Stop searching, but don't cache yet; call method 
                    // resolver for this class first.
                    break;
                }
            }
            
            // Superclass method list.
            // 在父類方法列表中查找,并緩存到父類的緩存中
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }
  • 3.2、動(dòng)態(tài)方法解析階段
動(dòng)態(tài)方法解析
    // No implementation found. Try method resolver once.
    // 判斷是否進(jìn)行過動(dòng)態(tài)方法解析,如果沒有解析過,則執(zhí)行括號(hào)中的內(nèi)容
    if (resolver  &&  !triedResolver) {
        runtimeLock.unlockRead();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.read();
        // Don't cache the result; we don't hold the lock so it may have 
        // changed already. Re-do the search from scratch instead.
        // 第一次解析后標(biāo)記,第二次就不會(huì)進(jìn)行解析了
        triedResolver = YES;
        // 回到消息發(fā)送階段
        goto retry;
    }

    // No implementation found, and method resolver didn't help. 
    // Use forwarding.
    // 消息轉(zhuǎn)發(fā)階段
    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlockRead();
void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        _class_resolveInstanceMethod(cls, sel, inst);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        _class_resolveClassMethod(cls, sel, inst);
        if (!lookUpImpOrNil(cls, sel, inst, 
                            NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) 
        {
            _class_resolveInstanceMethod(cls, sel, inst);
        }
    }
}
  • 3.2.1、動(dòng)態(tài)方法解析具體實(shí)現(xiàn)一:對(duì)象方法調(diào)用解析
@interface MJPerson : NSObject

- (void)test;

@end
#import "MJPerson.h"
#import <objc/runtime.h>

@implementation MJPerson

//- (void)test {
//    NSLog(@"%s", __func__);
//}

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

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        // 獲取其他方法
        Method otherThod = class_getInstanceMethod(self, @selector(other));
        
        // 動(dòng)態(tài)添加test方法實(shí)現(xiàn)
        class_addMethod(self,
                        sel,
                        method_getImplementation(otherThod),
                        method_getTypeEncoding(otherThod));
        
        // 返回yes代表有動(dòng)態(tài)添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [[MJPerson alloc] init];
        [person test];
    }
    return 0;
}

2019-09-30 19:45:46.065352+0800 Test12[18214:1032252] -[MJPerson other]
  • 3.2.2、類方法調(diào)用解析
@interface MJPerson : NSObject

+ (void)test;

@end
#import "MJPerson.h"
#import <objc/runtime.h>

@implementation MJPerson

//+ (void)test {
//    NSLog(@"%s", __func__);
//}

void c_other(id self, SEL _cmd) {
    NSLog(@"c_other -%@ -%@", self, NSStringFromSelector(_cmd));
}

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(test)) {
        // 第一個(gè)參數(shù)是 object_getClass(self)
        class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [MJPerson test];
    }
    return 0;
}

2019-09-30 19:48:41.825602+0800 Test12[18258:1034184] c_other -MJPerson -test
  • 3.2.3、C語言函數(shù)動(dòng)態(tài)解析
#import "MJPerson.h"
#import <objc/runtime.h>

@implementation MJPerson

//- (void)test {
//    NSLog(@"%s", __func__);
//}

void c_other(id self, SEL _cmd) {
    NSLog(@"c_other -%@ -%@", self, NSStringFromSelector(_cmd));
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        // 獲取其他方法
        class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
        // 返回yes代表有動(dòng)態(tài)添加方法
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

@end

  • 3.3、消息轉(zhuǎn)發(fā)
消息轉(zhuǎn)發(fā)流程
  • 3.3.1、消息轉(zhuǎn)發(fā)(一):forwardingTargetForSelector(MJPerson 消息由 MJStudent處理)
@interface MJPerson : NSObject

- (void)test;

@end


@implementation MJPerson

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        // objc_msgSend(student, aSelector);
        return [[MJStudent alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end
#import "MJStudent.h"

@implementation MJStudent

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

@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [[MJPerson alloc] init];
        [person test];
    }
    return 0;
}

2019-09-30 19:59:13.819509+0800 Test12[18418:1039881] -[MJStudent test]
  • 3.3.2、消息轉(zhuǎn)發(fā)(二):forwardInvocation
    MJPerson 的消息 forwardingTargetForSelector方法未能處理,進(jìn)入下一步轉(zhuǎn)發(fā)流程
@interface MJPerson : NSObject

- (void)test;

@end
#import "MJPerson.h"
#import "MJStudent.h"

@implementation MJPerson

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return nil;
    }
    return [super forwardingTargetForSelector:aSelector];
}

// 方法簽名:返回值、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    }
    return [super methodSignatureForSelector:aSelector];
}

//
/**
 NSInvocation 封裝了一個(gè)方法調(diào)用,包括方法調(diào)用者、方法名、方法參數(shù)
 
 anInvocation.target 方法調(diào)用者
 anInvocation.selector 方法名
 [anInvocation getArgument:NULL atIndex:0] 方法參數(shù)
 */
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    /**
     方法一
     
     anInvocation.target = [[MJStudent alloc] init];
     [anInvocation invoke];
     */
    
    // 方法二
    [anInvocation invokeWithTarget:[[MJStudent alloc] init]];
}

@end
#import "MJStudent.h"

@implementation MJStudent

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

@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [[MJPerson alloc] init];
        [person test];
    }
    return 0;
}

2019-09-30 20:08:02.735260+0800 Test12[18511:1043993] -[MJStudent test]

四、番外篇

  • 4.1、類方法轉(zhuǎn)發(fā)到對(duì)象方法
#import "MJStudent.h"

@implementation MJStudent

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

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

@end
#import "MJPerson.h"
#import "MJStudent.h"

@implementation MJPerson

+ (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        /**
         objc_msgSend([[MJStudent alloc] init], @selector(test));
         [[[MJStudent alloc] init] test]
         */
        return [[MJStudent alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [MJPerson test];
    }
    return 0;
}

2019-09-05 21:05:50.180158+0800 test[40754:500893] -[MJStudent test]
  • 4.2、動(dòng)態(tài)實(shí)現(xiàn) set、get方法
@interface MJPerson : NSObject

@property (nonatomic, assign) int age;

@end
#import "MJPerson.h"
#import <objc/runtime.h>

@implementation MJPerson

@dynamic age;

void setAge(id self, SEL _cmd, int age) {
    NSLog(@"age is %d", age);
}

int age(id self, SEL _cmd) {
    return 120;
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(setAge:)) {
        class_addMethod(self, sel, (IMP)setAge, "v@:I");
        return YES;
    } else if (sel == @selector(age)) {
        class_addMethod(self, sel, (IMP)age, "i@:I");
    }
    return [super resolveInstanceMethod:sel];
}

@end
#import <Foundation/Foundation.h>
#import "MJPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJPerson *person = [[MJPerson alloc] init];
        person.age = 20;
        NSLog(@"person.age is %d", person.age);
    }
    return 0;
}

2019-09-07 18:40:20.740030+0800 test[1514:27406] age is 20
2019-09-07 18:40:20.740276+0800 test[1514:27406] person.age is 120
最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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