1.準備測試代碼:c++
#import <Foundation/Foundation.h>
@interface ABPerson : NSObject
-(void)saySomething;
@end
@implementation ABPerson
-(void)saySomething
{
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ABPerson *p = [ABPerson alloc];
[p saySomething];
NSLog(@"Hello, World!");
}
return 0;
}
複製代碼
2.編譯成.pp文件
git
clang -rewrite-objc main.m -o main.cpp
複製代碼
3.打開main.cpp文件分析: 在入口
main
方法中,調用了alloc
方法和saySomething
方法,在產生的c++
代碼中,都調用了objc_msgSend
這個方法,而且傳了兩個參數,第一個是消息的接受者(id
),第二個是方法編號(SEL
)。github
消息發送的函數定義以下:markdown
id objc_msgSend(id self, SEL _cmd, ...);
複製代碼
因此他仍是能夠傳更多的參數的架構
4.修改函數,添加兩個參數函數
#import <Foundation/Foundation.h>
@interface ABPerson : NSObject
-(void)saySomething:(NSString *)a b:(NSString *)b;
@end
@implementation ABPerson
-(void)saySomething:(NSString *)a b:(NSString *)b
{
NSLog(@"%s",__func__);
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ABPerson *p = [ABPerson alloc];
[p saySomething:@"A" b:@"B"];
NSLog(@"Hello, World!");
}
return 0;
}
複製代碼
5.從新編譯,並查看函數調用 oop
因此結論就是:方法的本質就是消息的發送測試
既然方法調用的本質就是經過objc_msgSend
發送消息,那麼就用它來調用方法。ui
1.導入頭文件spa
#import <objc/message.h>
複製代碼
調用方法代碼:
objc_msgSend(p,@selector(saySomething));
複製代碼
若是報錯,配置一下紅框位置,請其設置爲
NO
objc_msgSendSuper
定義:
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ ) 複製代碼
objc_msgSendSuper
包含的頭兩個參數分別是結構體objc_super
和方法編號SEL
結構體objc_super
定義:
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
複製代碼
包含了一個接受者receiver
和一個super_class
1.定義一個ABTeacher
繼承ABPerson
,並實現doSomething
方法
@interface ABTeacher : ABPerson
-(void)doSomething;
@end
@implementation ABTeacher
-(void)doSomething
{
NSLog(@"%s",__func__);
}
@end
複製代碼
ABTeacher *t = [ABTeacher alloc];
struct objc_super ab_objc_super;
ab_objc_super.receiver = t;
ab_objc_super.super_class = ABTeacher.class;
objc_msgSendSuper(&ab_objc_super,@selector(doSomething));
複製代碼
ab_objc_super.super_class
是從哪一個類開始查找,若是改爲ABPerson.class
,就是從ABPerson
查找,若是ABPerson
沒實現就拋出異常,也不會再查找ABTeacher
。
打開objc源碼找到
objc_msgSend
是在源碼中彙編實現: 查看arm64
架構的
cmp p0,
:判斷當前消息接收者是否爲0
b.le LNilOrTagged
:若是支持tagged pointer
,走這步b.eq LReturnZero
:不支持tagged pointer
,走這步,返回0
ldr p13, [x0]
:將消息接收者isa
給p13
GetClassFromIsa_p16 p13, 1, x0
:將消息接收者的class
存到p16
GetClassFromIsa_p16
是如何經過isa
找到class
的?
由於
GetClassFromIsa_p16
p13
, 1
, x0
,因此src
是p13
也就是isa
,needs_auth
是1
執行ExtractISA
,ExtractISA
就是將isa&ISA_MASK
的結果給$0
,也就是將拿到的class
給p16
。
上面的過程就是經過消息接收者獲取到class
。
(未完)
代碼準備:
#import <Foundation/Foundation.h>
@protocol ABPersonDelegate <NSObject>
-(void)masterNB;
@end
@interface ABPerson : NSObject<ABPersonDelegate>
-(void)saySomething;
@end
複製代碼
#import "ABPerson.h"
@implementation ABPerson
-(void)masterNB
{
NSLog(@"%s",__func__);
}
-(void)saySomething
{
NSLog(@"%s",__func__);
}
@end
複製代碼
p/x ABPerson.class
:打印Class
首地址p (class_data_bits_t *)0x0000000100008868
:首地址偏移32
個字節拿到bits
並將地址強轉成class_data_bits_t
類型p $1->data()
:調用class_data_bits_t
中的data()
函數p *$2
:打印data
p $3.protocols()
:調用protocols()
,獲取protocol
列表p $4.list
:拿到protocol_list_t
首地址p $5.ptr
:打印protocol_list_t
首地址p *$6
:打印protocol_list_t
,發現count=1
p $7.list[0]
:打印第一個p (protocol_t *)$8
:強轉成protocol_t
類型p *$9
:查看protocol_t
結構並找到 mangledName
爲ABPersonDelegate
p $10.instanceMethods
:獲取實例方法p $11.get(0).big()
:讀取 method_list_t
中的實例方法