前言:本文探究iOS中Runtime相關(guān)內(nèi)容,如有錯(cuò)誤請(qǐng)留言指正。
- Objective-C是一門動(dòng)態(tài)性比較強(qiáng)的編程語言,跟C、C++等語言有著很大的不同
- Objective-C的動(dòng)態(tài)性是由Runtime API來支撐的
- Runtime API提供的接口基本都是C語言的,源碼由C\C++\匯編語言編寫
- OC是一門動(dòng)態(tài)性比較強(qiáng)的編程語言,允許很多操作推遲到程序運(yùn)行時(shí)再進(jìn)行
- OC的動(dòng)態(tài)性就是由Runtime來支撐和實(shí)現(xiàn)的,Runtime是一套C語言的API,封裝了很多動(dòng)態(tài)性相關(guān)的函數(shù)
- 平時(shí)編寫的OC代碼,底層都是轉(zhuǎn)換成了Runtime API進(jìn)行調(diào)用
第一部分:isa
前一篇:iOS-對(duì)象、isa和SuperClass
- 按位與(&),同為1才為1
- 按位或(|),只要有1就是1
- 按位取反(~),1置為0,0置為1
- 取反運(yùn)算符(?。?,判斷表達(dá)式是否為真,為真取假,為假取真
什么是isa?
在arm64架構(gòu)之前,isa就是一個(gè)普通的指針,存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址
從arm64架構(gòu)開始,對(duì)isa進(jìn)行了優(yōu)化,變成了一個(gè)共用體(union)結(jié)構(gòu),還使用位域來存儲(chǔ)更多的信息
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
};
};
isa-位域詳解
1.nonpointer
0,代表普通的指針,存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址
1,代表優(yōu)化過,使用位域存儲(chǔ)更多的信息
2.has_assoc
是否有設(shè)置過關(guān)聯(lián)對(duì)象,如果沒有,釋放時(shí)會(huì)更快
3.has_cxx_dtor
是否有C++的析構(gòu)函數(shù)(.cxx_destruct),如果沒有,釋放時(shí)會(huì)更快
4.shiftcls
存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址信息
5.magic
用于在調(diào)試時(shí)分辨對(duì)象是否未完成初始化
6.weakly_referenced
是否有被弱引用指向過,如果沒有,釋放時(shí)會(huì)更快
7.deallocating
對(duì)象是否正在釋放
8.extra_rc
里面存儲(chǔ)的值是引用計(jì)數(shù)器減1
9.has_sidetable_rc
引用計(jì)數(shù)器是否過大無法存儲(chǔ)在isa中
如果為1,那么引用計(jì)數(shù)會(huì)存儲(chǔ)在一個(gè)叫SideTable的類的屬性中
釋放對(duì)象調(diào)用的方法
/***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
代碼可以看出釋放對(duì)象回判斷是否有C++函數(shù)和關(guān)聯(lián)對(duì)象,如果有會(huì)進(jìn)一步做釋放工作。
知識(shí)點(diǎn):
- Class、meta-class 對(duì)象的地址值(二進(jìn)制)最后2位為000
- 通過isa指針可以知道是否有關(guān)聯(lián)對(duì)象、是否被弱引用過
Class是結(jié)構(gòu)體結(jié)構(gòu)

class_rw_t
class_rw_t里面的methods、properties、protocols是二維數(shù)組,是可讀可寫的,包含了類的初始內(nèi)容、分類的內(nèi)容

class_ro_t
class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一維數(shù)組,是只讀的,包含了類的初始內(nèi)容

第二部分:Runtime-方法
Q:簡(jiǎn)述method_t
method_t是對(duì)方法\函數(shù)的封裝
源碼展示:
struct method_t {
SEL name;//函數(shù)名
const char *types;//編碼(返回值類型、參數(shù)類型)
IMP imp;//指向函數(shù)的指針(函數(shù)地址)
}
1.SEL
- SEL代表方法\函數(shù)名,一般叫做選擇器,底層結(jié)構(gòu)跟char *類似
- 可以通過
@selector()和sel_registerName()獲得 - 可以通過sel_getName()和NSStringFromSelector()轉(zhuǎn)成字符串
- 不同類中相同名字的方法,所對(duì)應(yīng)的方法選擇器是相同的
typedef struct objc_selector*SEL;
2.IMP
IMP代表函數(shù)的具體實(shí)現(xiàn)
typedef id_Nullable (*IMP) (id_Nonnull,SEl_Nonnull,...);
3.types
types包含了函數(shù)返回值、參數(shù)編碼的字符串

函數(shù)類型:
- (int)test:(int)age height:(float)height;
types顯示內(nèi)容
"i24@0:8i16f20"
types返回內(nèi)容逐字解釋:
- i:表示返回值 int
- @:表示id類型
- “:”:表示SEL
- f:表示 float
- 24:表示總共占中24個(gè)字節(jié),id(8)+SEL(8)+int(4)+float(4) = 24
- 0:表示id類型從0字節(jié)開始
- 8:表示SEL類型從8字節(jié)開始
- 16:表示age(int)參數(shù)從16字節(jié)開始
- 20:表示height(float)參數(shù)從20字節(jié)開始
iOS中提供了一個(gè)叫做@encode的指令,可以將具體的類型表示成字符串編碼,如下:


Q:什么是方法緩存?
Class內(nèi)部結(jié)構(gòu)中有個(gè)方法緩存(cache_t),用散列表(哈希表)來緩存曾經(jīng)調(diào)用過的方法,可以提高方法的查找速度。

Q:什么是散列表緩存?
- 在緩存和取值時(shí)有個(gè)策略:
@selector(personTest) & _mask = 索引值 - 根據(jù)這種策略把方法緩存到列表中,如果索引值相同就
索引值-1,如果減少到0,就從_mask開始存放 - 在取值時(shí)根據(jù)這種策略來直接找到索引值,判斷該索引值存放的key是否相同,相同取出IMP;不相同,查找
索引值-1 -> 0 ->_mask -> _mask-1 -> ···直到找到相同的key,取出IMP - 優(yōu)點(diǎn):犧牲內(nèi)存空間換取讀取時(shí)間,效率高
- 一旦數(shù)組擴(kuò)容,就會(huì)把緩存清掉,擴(kuò)容數(shù)組容量 = 舊數(shù)組容量 * 2
- 可自行參考其他博客查看
第三部分:Runtime-objc_msgSend
objc_msgSend執(zhí)行流程分為三大階段:消息發(fā)送、動(dòng)態(tài)方法解析、消息轉(zhuǎn)發(fā)
Runtime源碼解讀流程
objc-msg-arm64.s
ENTRY _objc_msgSend
b.le LNilOrTagged
CacheLookup NORMAL
.macro CacheLookup
.macro CheckMiss
STATIC_ENTRY __objc_msgSend_uncached
.macro MethodTableLookup
__class_lookupMethodAndLoadCache3
objc-runtime-new.mm
_class_lookupMethodAndLoadCache3
lookUpImpOrForward
getMethodNoSuper_nolock、search_method_list、log_and_fill_cache
cache_getImp、log_and_fill_cache、getMethodNoSuper_nolock、log_and_fill_cache
_class_resolveInstanceMethod
_objc_msgForward_impcache
objc-msg-arm64.s
STATIC_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
Core Foundation
__forwarding__(不開源)
3.1 消息發(fā)送

- receiver通過isa指針找到receiverClass
- receiverClass通過superclass指針找到superClass
- 如果是從class_rw_t中查找方法
1.已經(jīng)排序的,二分查找
2.沒有排序的,遍歷查找
3.2 動(dòng)態(tài)解析

開發(fā)者可以實(shí)現(xiàn)以下方法,來動(dòng)態(tài)添加方法實(shí)現(xiàn)
- +resolveInstanceMethod:添加對(duì)象方法
- +resolveClassMethod:添加類方法
動(dòng)態(tài)解析過后,會(huì)重新走“消息發(fā)送”的流程
- “從receiverClass的cache中查找方法”這一步開始執(zhí)行
Q:Runtime動(dòng)態(tài)添加方法的幾種方式?
Runtime方式
- (void)other{
NSLog(@"%s",__func__);
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"%s",__func__);
if (sel == @selector(test)) {
Method method = class_getInstanceMethod(self, @selector(other));
//動(dòng)態(tài)添加對(duì)象方法,需要給類添加方法
class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
return YES;
}
return [super resolveInstanceMethod:sel];
}
OC 方式
- (void)other{
NSLog(@"%s",__func__);
}
struct method_t {
SEL sel;
char *types;
IMP imp;
};
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 獲取其他方法
struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
// 動(dòng)態(tài)添加test方法的實(shí)現(xiàn)
class_addMethod(self, sel, method->imp, method->types);
// 返回YES代表有動(dòng)態(tài)添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
C 方法
void c_other(id self, SEL _cmd)
{
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 動(dòng)態(tài)添加test方法的實(shí)現(xiàn)
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
// 返回YES代表有動(dòng)態(tài)添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
動(dòng)態(tài)添加類方法
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 resolveClassMethod:sel];
}
Q:dynamic修飾變量的含義?
@dynamic是告訴編譯器不用自動(dòng)生成getter和setter的實(shí)現(xiàn),等到運(yùn)行時(shí)再添加方法實(shí)現(xiàn)
提醒編譯器不要自動(dòng)生成setter和getter的實(shí)現(xiàn)、不要自動(dòng)生成成員變量
@interface Person : NSObject
@property (nonatomic, assign) int age;
@end
@implementation Person
@dynamic age;
@end
3.3 消息轉(zhuǎn)發(fā)

消息轉(zhuǎn)發(fā)底層匯編轉(zhuǎn)OC(某位大神制作)
int __forwarding__(void *frameStackPointer, int isStret) {
id receiver = *(id *)frameStackPointer;
SEL sel = *(SEL *)(frameStackPointer + 8);
const char *selName = sel_getName(sel);
Class receiverClass = object_getClass(receiver);
// 調(diào)用 forwardingTargetForSelector:
if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
id forwardingTarget = [receiver forwardingTargetForSelector:sel];
if (forwardingTarget && forwardingTarget != receiver) {
return objc_msgSend(forwardingTarget, sel, ...);
}
}
// 調(diào)用 methodSignatureForSelector 獲取方法簽名后再調(diào)用 forwardInvocation
if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
if (methodSignature && class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
[receiver forwardInvocation:invocation];
void *returnValue = NULL;
[invocation getReturnValue:&value];
return returnValue;
}
}
if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {
[receiver doesNotRecognizeSelector:sel];
}
// The point of no return.
kill(getpid(), 9);
}
3.3.1 指派其他對(duì)象的方法來完成Person中test方法的調(diào)用
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
// objc_msgSend([[Cat alloc] init], aSelector)
return [[Cat alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
3.3.2 未實(shí)現(xiàn)forwardingTargetForSelector,消息轉(zhuǎn)發(fā)
// 方法簽名:返回值類型、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
//types的規(guī)則一定要和anInvocation一一對(duì)應(yīng)
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
// return [[[Cat alloc] init] methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
//該方法可實(shí)現(xiàn)指派對(duì)象、指派方法,也可以什么都不做
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// anInvocation.target = [[Cat alloc] init];
// [anInvocation invoke];
[anInvocation invokeWithTarget:[[Cat alloc] init]];
}
// NSInvocation封裝了一個(gè)方法調(diào)用,包括:方法調(diào)用者、方法名、方法參數(shù)
// anInvocation.target 方法調(diào)用者
// anInvocation.selector 方法名
// [anInvocation getArgument:NULL atIndex:0]
3.3.3 處理類方法的消息轉(zhuǎn)發(fā)
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
if (aSelector == @selector(test))
return [Cat class];//類方法存在元類中
return [super forwardingTargetForSelector:aSelector];
}
//如果沒有實(shí)現(xiàn)+ (id)forwardingTargetForSelector:(SEL)aSelector方法
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test))
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"1123");
}
對(duì)類方法消息轉(zhuǎn)發(fā)可采用類對(duì)象,不一定要使用元類對(duì)象
原因:消息轉(zhuǎn)發(fā)objc_msgSend只看消息接收者和方法名
+ (id)forwardingTargetForSelector:(SEL)aSelector
{
// objc_msgSend([[MJCat alloc] init], @selector(test))
// [[[Cat alloc] init] test]
if (aSelector == @selector(test))
return [[MJCat alloc] init];
return [super forwardingTargetForSelector:aSelector];
}
第四部分:Runtime-super/class
Q:下列代碼輸出結(jié)果是什么?
Student 類的.m實(shí)現(xiàn) Student的父類是Person
- (instancetype)init{
if (self = [super init]) {
NSLog(@"[self class] = %@",[self class]);
NSLog(@"[self superclass] = %@",[self superclass]);
NSLog(@"[super class] = %@",[super class]);
NSLog(@"[super superclass] = %@",[super superclass]);
}
return self;
}
輸出結(jié)果:
[self class] = Student
[self superclass] = Person
[super class] = Student
[super superclass] = Person
理解下super的調(diào)用
[super message]的底層實(shí)現(xiàn)
1.消息接收者仍然是子類對(duì)象
2.從父類開始查找方法的實(shí)現(xiàn)
3.super:消息接收者仍然是當(dāng)前類對(duì)象,只是從父類查找方法的實(shí)現(xiàn)
class和superclass
1.方法實(shí)現(xiàn)在NSObject上
2.class方法作用:返回當(dāng)前對(duì)象的類對(duì)象
3.superclass方法作用:返回當(dāng)前對(duì)象的父類對(duì)象
struct objc_super {
__unsafe_unretained _Nonnull id receiver; // 消息接收者
__unsafe_unretained _Nonnull Class super_class; // 消息接收者的父類
};
Q:下列代碼輸出結(jié)果?
BOOL res1 = [[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [[Person class] isKindOfClass:[Person class]];
BOOL res4 = [[Person class] isMemberOfClass:[Person class]];
BOOL res5 = [[Person class] isMemberOfClass:[NSObject class]];
NSLog(@"r1:%d r2:%d r3:%d r4:%d r5:%d",res1,res2,res3,res4,res5);
輸出結(jié)果:
r1:1 r2:0 r3:0 r4:0 r5:0
- -isMemberOfClass:直接返回兩個(gè)類是否相等
- -isKindOfClass:判斷調(diào)用方法類是否是傳入方法的子類。
- +isMemberOfClass:判斷調(diào)用類的
元類是否相等 - +isKindOfClass:判斷調(diào)用類的
元類是否是傳入類的子類。 - 兩者都能檢測(cè)一個(gè)對(duì)象是否是某個(gè)類的成員, 兩者之間的區(qū)別是:isKindOfClass不但可以用來確定一個(gè)對(duì)象是否是一個(gè)類的成員,也可以用來確定一個(gè)對(duì)象是否是派生自該類的類的成員 ,而isMemberOfClass做不到后一點(diǎn)。
比如classA派生自NSObject類,classA* x = [classA new]; [x isKindOfClass:[NSObject class]] 可以檢查出x是否是NSObject派生類的成員,但isMemberOfClass做不到。
NSObject.mm部分源碼
+ (void)load {
}
+ (void)initialize {
}
+ (id)self {
return (id)self;
}
- (id)self {
return self;
}
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
+ (Class)superclass {
return self->superclass;
}
- (Class)superclass {
return [self class]->superclass;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isSubclassOfClass:(Class)cls {
for (Class tcls = self; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isAncestorOfObject:(NSObject *)obj {
for (Class tcls = [obj class]; tcls; tcls = tcls->superclass) {
if (tcls == self) return YES;
}
return NO;
}
Q:super的本質(zhì)?
super調(diào)用,底層會(huì)轉(zhuǎn)換為objc_msgSendSuper2函數(shù)的調(diào)用,接收2個(gè)參數(shù):struct objc_super2 和 SEL
struct objc_super2 {
id receiver;//是消息接收者
Class current_class;//是receiver的Class對(duì)象
};
第五部分:Runtime-API應(yīng)用
Q:消息轉(zhuǎn)發(fā)應(yīng)用于哪里?
代碼示例:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
// 本來能調(diào)用的方法
if ([self respondsToSelector:aSelector]) {
return [super methodSignatureForSelector:aSelector];
}
// 找不到的方法
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
// 找不到的方法,都會(huì)來到這里
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//處理傳錯(cuò)方法或未實(shí)現(xiàn)的方法
NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}
Q:項(xiàng)目中Runtime應(yīng)用?
- 可用統(tǒng)計(jì)和處理傳錯(cuò)方法或未實(shí)現(xiàn)的方法
- 利用關(guān)聯(lián)對(duì)象(AssociatedObject)給分類添加屬性
- 遍歷類的所有成員變量(修改textfield的占位文字顏色、字典轉(zhuǎn)模型、自動(dòng)歸檔解檔)
- 交換方法實(shí)現(xiàn)(交換系統(tǒng)的方法)
- 利用消息轉(zhuǎn)發(fā)機(jī)制解決方法找不到的異常問題
1.Runtime-API-類
動(dòng)態(tài)創(chuàng)建一個(gè)類(參數(shù):父類,類名,額外的內(nèi)存空間)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
注冊(cè)一個(gè)類(要在類注冊(cè)之前添加成員變量)
void objc_registerClassPair(Class cls)
銷毀/釋放一個(gè)類
void objc_disposeClassPair(Class cls)
獲取isa指向的Class
Class object_getClass(id obj)
設(shè)置isa指向的Class
Class object_setClass(id obj, Class cls)
判斷一個(gè)OC對(duì)象是否為Class
BOOL object_isClass(id obj)
判斷一個(gè)Class是否為元類
BOOL class_isMetaClass(Class cls)
獲取父類
Class class_getSuperclass(Class cls)
2.Runtime-API-成員變量
獲取一個(gè)實(shí)例變量信息
Ivar class_getInstanceVariable(Class cls, const char *name)
拷貝實(shí)例變量列表(最后需要調(diào)用free釋放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
設(shè)置和獲取成員變量的值
void object_setIvar(id obj, Ivar ivar, id value)
object_setIvar(person,ageIvar,(__bridge id)(void *)10)//age賦值為10
id object_getIvar(id obj, Ivar ivar)
動(dòng)態(tài)添加成員變量(已經(jīng)注冊(cè)的類是不能動(dòng)態(tài)添加成員變量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
獲取成員變量的相關(guān)信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
3.Runtime-API-屬性
獲取一個(gè)屬性
objc_property_t class_getProperty(Class cls, const char *name)
拷貝屬性列表(最后需要調(diào)用free釋放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
動(dòng)態(tài)添加屬性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
動(dòng)態(tài)替換屬性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
獲取屬性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
4.Runtime-API-方法
獲得一個(gè)實(shí)例方法、類方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
方法實(shí)現(xiàn)相關(guān)操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
拷貝方法列表(最后需要調(diào)用free釋放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
動(dòng)態(tài)添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
動(dòng)態(tài)替換方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
獲取方法的相關(guān)信息(帶有copy的需要調(diào)用free去釋放)
- SEL method_getName(Method m)
- IMP method_getImplementation(Method m)
- const char *method_getTypeEncoding(Method m)
- unsigned int method_getNumberOfArguments(Method m)
- char *method_copyReturnType(Method m)
- char *method_copyArgumentType(Method m, unsigned int index)
選擇器相關(guān)
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
用block作為方法實(shí)現(xiàn)
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
Q:如何交換方法?
void method_exchangeImplementations(Method m1, Method m2)
- 方法交換操作,實(shí)際是把method_t的IMP交換了
- 自己的方法交換系統(tǒng)的方法(hook):自己的Method需要再次調(diào)用自己的Method(已經(jīng)是系統(tǒng)方法了)
- 獲取方法時(shí)候一定要選擇當(dāng)前類的真實(shí)類來獲取方法(class_getInstanceMethod)
- 類簇:NSString、NSArray、NSDictionary,真實(shí)類型是其他類型
補(bǔ)充:LLVM的中間代碼(IR)
Objective-C在變?yōu)闄C(jī)器代碼之前,會(huì)被LLVM編譯器轉(zhuǎn)換為中間代碼(Intermediate Representation)
可以使用以下命令行指令生成中間代碼
clang -emit-llvm -S main.m
語法簡(jiǎn)介:
@ - 全局變量
% - 局部變量
alloca - 在當(dāng)前執(zhí)行的函數(shù)的堆棧幀中分配內(nèi)存,當(dāng)該函數(shù)返回到其調(diào)用者時(shí),將自動(dòng)釋放內(nèi)存
i32 - 32位4字節(jié)的整數(shù)
align - 對(duì)齊
load - 讀出,store 寫入
icmp - 兩個(gè)整數(shù)值比較,返回布爾值
br - 選擇分支,根據(jù)條件來轉(zhuǎn)向label,不根據(jù)條件跳轉(zhuǎn)的話類似 goto
label - 代碼標(biāo)簽
call - 調(diào)用函數(shù)
具體可以參考官方文檔:https://llvm.org/docs/LangRef.html