前言:本文探究iOS中Runtime相關(guān)內(nèi)容都许,如有錯誤請留言指正陌僵。
- Objective-C是一門動態(tài)性比較強(qiáng)的編程語言惨撇,跟C、C++等語言有著很大的不同
- Objective-C的動態(tài)性是由Runtime API來支撐的
- Runtime API提供的接口基本都是C語言的晓殊,源碼由C\C++\匯編語言編寫
- OC是一門動態(tài)性比較強(qiáng)的編程語言断凶,允許很多操作推遲到程序運(yùn)行時再進(jìn)行
- OC的動態(tài)性就是由Runtime來支撐和實現(xiàn)的,Runtime是一套C語言的API巫俺,封裝了很多動態(tài)性相關(guān)的函數(shù)
- 平時編寫的OC代碼认烁,底層都是轉(zhuǎn)換成了Runtime API進(jìn)行調(diào)用
第一部分:isa
前一篇:iOS-對象、isa和SuperClass
- 按位與(&)介汹,同為1才為1
- 按位或(|)却嗡,只要有1就是1
- 按位取反(~),1置為0痴昧,0置為1
- 取反運(yùn)算符(;隆),判斷表達(dá)式是否為真赶撰,為真取假舌镶,為假取真
什么是isa?
在arm64架構(gòu)之前豪娜,isa就是一個普通的指針餐胀,存儲著Class、Meta-Class對象的內(nèi)存地址
從arm64架構(gòu)開始瘤载,對isa進(jìn)行了優(yōu)化否灾,變成了一個共用體(union
)結(jié)構(gòu),還使用位域來存儲更多的信息
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鸣奔,代表普通的指針墨技,存儲著Class、Meta-Class對象的內(nèi)存地址
1挎狸,代表優(yōu)化過扣汪,使用位域存儲更多的信息
2.has_assoc
是否有設(shè)置過關(guān)聯(lián)對象,如果沒有锨匆,釋放時會更快
3.has_cxx_dtor
是否有C++的析構(gòu)函數(shù)(.cxx_destruct)崭别,如果沒有,釋放時會更快
4.shiftcls
存儲著Class恐锣、Meta-Class對象的內(nèi)存地址信息
5.magic
用于在調(diào)試時分辨對象是否未完成初始化
6.weakly_referenced
是否有被弱引用指向過茅主,如果沒有,釋放時會更快
7.deallocating
對象是否正在釋放
8.extra_rc
里面存儲的值是引用計數(shù)器減1
9.has_sidetable_rc
引用計數(shù)器是否過大無法存儲在isa中
如果為1土榴,那么引用計數(shù)會存儲在一個叫SideTable的類的屬性中
釋放對象調(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;
}
代碼可以看出釋放對象回判斷是否有C++函數(shù)和關(guān)聯(lián)對象诀姚,如果有會進(jìn)一步做釋放工作。
知識點(diǎn):
- Class鞭衩、meta-class 對象的地址值(二進(jìn)制)最后2位為000
- 通過isa指針可以知道是否有關(guān)聯(lián)對象学搜、是否被弱引用過
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:簡述method_t
method_t是對方法\函數(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)成字符串
- 不同類中相同名字的方法撤摸,所對應(yīng)的方法選擇器是相同的
typedef struct objc_selector*SEL;
2.IMP
IMP代表函數(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個字節(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中提供了一個叫做@encode的指令准夷,可以將具體的類型表示成字符串編碼钥飞,如下:
Q:什么是方法緩存?
Class內(nèi)部結(jié)構(gòu)中有個方法緩存(cache_t)衫嵌,用散列表(哈希表)
來緩存曾經(jīng)調(diào)用過的方法读宙,可以提高方法的查找速度。
Q:什么是散列表緩存楔绞?
- 在緩存和取值時有個策略:
@selector(personTest) & _mask = 索引值
- 根據(jù)這種策略把方法緩存到列表中结闸,如果索引值相同就
索引值-1
,如果減少到0酒朵,就從_mask
開始存放 - 在取值時根據(jù)這種策略來直接找到索引值桦锄,判斷該索引值存放的key是否相同,相同取出IMP耻讽;不相同察纯,查找
索引值-1 -> 0 ->_mask -> _mask-1 -> ···
直到找到相同的key,取出IMP - 優(yōu)點(diǎn):犧牲內(nèi)存空間換取讀取時間针肥,效率高
- 一旦數(shù)組擴(kuò)容饼记,就會把緩存清掉,擴(kuò)容數(shù)組容量 = 舊數(shù)組容量 * 2
- 可自行參考其他博客查看
第三部分:Runtime-objc_msgSend
objc_msgSend執(zhí)行流程分為三大階段:消息發(fā)送慰枕、動態(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 動態(tài)解析
開發(fā)者可以實現(xiàn)以下方法,來動態(tài)添加方法實現(xiàn)
- +resolveInstanceMethod:添加對象方法
- +resolveClassMethod:添加類方法
動態(tài)解析過后病游,會重新走“消息發(fā)送”的流程
- “從receiverClass的cache中查找方法”這一步開始執(zhí)行
Q:Runtime動態(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));
//動態(tài)添加對象方法,需要給類添加方法
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));
// 動態(tài)添加test方法的實現(xiàn)
class_addMethod(self, sel, method->imp, method->types);
// 返回YES代表有動態(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)) {
// 動態(tài)添加test方法的實現(xiàn)
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
// 返回YES代表有動態(tài)添加方法
return YES;
}
return [super resolveInstanceMethod:sel];
}
動態(tài)添加類方法
void c_other(id self, SEL _cmd)
{
NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
if (sel == @selector(test)) {
// 第一個參數(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是告訴編譯器不用自動生成getter和setter的實現(xiàn)买猖,等到運(yùn)行時再添加方法實現(xiàn)
提醒編譯器不要自動生成setter和getter的實現(xiàn)、不要自動生成成員變量
@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 指派其他對象的方法來完成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 未實現(xiàn)forwardingTargetForSelector
滋尉,消息轉(zhuǎn)發(fā)
// 方法簽名:返回值類型玉控、參數(shù)類型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if (aSelector == @selector(test)) {
//types的規(guī)則一定要和anInvocation一一對應(yīng)
return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
// return [[[Cat alloc] init] methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
//該方法可實現(xiàn)指派對象、指派方法狮惜,也可以什么都不做
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
// anInvocation.target = [[Cat alloc] init];
// [anInvocation invoke];
[anInvocation invokeWithTarget:[[Cat alloc] init]];
}
// NSInvocation封裝了一個方法調(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];
}
//如果沒有實現(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");
}
對類方法消息轉(zhuǎn)發(fā)可采用類對象懒叛,不一定要使用元類對象
原因:消息轉(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實現(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]的底層實現(xiàn)
1.消息接收者仍然是子類對象
2.從父類開始查找方法的實現(xiàn)
3.super:消息接收者仍然是當(dāng)前類對象耽梅,只是從父類查找方法的實現(xiàn)
class和superclass
1.方法實現(xiàn)在NSObject上
2.class方法作用:返回當(dāng)前對象的類對象
3.superclass方法作用:返回當(dāng)前對象的父類對象
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:直接返回兩個類是否相等
- -isKindOfClass:判斷調(diào)用方法類是否是傳入方法的子類。
- +isMemberOfClass:判斷調(diào)用類的
元類
是否相等 - +isKindOfClass:判斷調(diào)用類的
元類
是否是傳入類的子類眼姐。 - 兩者都能檢測一個對象是否是某個類的成員诅迷, 兩者之間的區(qū)別是:isKindOfClass不但可以用來確定一個對象是否是一個類的成員,也可以用來確定一個對象是否是派生自該類的類的成員 众旗,而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)用利朵,底層會轉(zhuǎn)換為objc_msgSendSuper2函數(shù)的調(diào)用律想,接收2個參數(shù):struct objc_super2
和 SEL
struct objc_super2 {
id receiver;//是消息接收者
Class current_class;//是receiver的Class對象
};
第五部分: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@:"];
}
// 找不到的方法绍弟,都會來到這里
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//處理傳錯方法或未實現(xiàn)的方法
NSLog(@"找不到%@方法", NSStringFromSelector(anInvocation.selector));
}
Q:項目中Runtime應(yīng)用技即?
- 可用統(tǒng)計和處理傳錯方法或未實現(xiàn)的方法
- 利用關(guān)聯(lián)對象(AssociatedObject)給分類添加屬性
- 遍歷類的所有成員變量(修改textfield的占位文字顏色、字典轉(zhuǎn)模型樟遣、自動歸檔解檔)
- 交換方法實現(xiàn)(交換系統(tǒng)的方法)
- 利用消息轉(zhuǎn)發(fā)機(jī)制解決方法找不到的異常問題
1.Runtime-API-類
動態(tài)創(chuàng)建一個類(參數(shù):父類而叼,類名身笤,額外的內(nèi)存空間)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
注冊一個類(要在類注冊之前添加成員變量)
void objc_registerClassPair(Class cls)
銷毀/釋放一個類
void objc_disposeClassPair(Class cls)
獲取isa指向的Class
Class object_getClass(id obj)
設(shè)置isa指向的Class
Class object_setClass(id obj, Class cls)
判斷一個OC對象是否為Class
BOOL object_isClass(id obj)
判斷一個Class是否為元類
BOOL class_isMetaClass(Class cls)
獲取父類
Class class_getSuperclass(Class cls)
2.Runtime-API-成員變量
獲取一個實例變量信息
Ivar class_getInstanceVariable(Class cls, const char *name)
拷貝實例變量列表(最后需要調(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)
動態(tài)添加成員變量(已經(jī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-屬性
獲取一個屬性
objc_property_t class_getProperty(Class cls, const char *name)
拷貝屬性列表(最后需要調(diào)用free釋放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
動態(tài)添加屬性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
unsigned int attributeCount)
動態(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-方法
獲得一個實例方法、類方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)
方法實現(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)
動態(tài)添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
動態(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作為方法實現(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)
- 方法交換操作,實際是把method_t的IMP交換了
- 自己的方法交換系統(tǒng)的方法(hook):自己的Method需要再次調(diào)用自己的Method(已經(jīng)是系統(tǒng)方法了)
- 獲取方法時候一定要選擇當(dāng)前類的真實類來獲取方法(class_getInstanceMethod)
- 類簇:NSString腔彰、NSArray吏恭、NSDictionary尉咕,真實類型是其他類型
補(bǔ)充:LLVM的中間代碼(IR)
Objective-C在變?yōu)闄C(jī)器代碼之前蟋恬,會被LLVM編譯器轉(zhuǎn)換為中間代碼(Intermediate Representation)
可以使用以下命令行指令生成中間代碼
clang -emit-llvm -S main.m
語法簡介:
@ - 全局變量
% - 局部變量
alloca - 在當(dāng)前執(zhí)行的函數(shù)的堆棧幀中分配內(nèi)存涤久,當(dāng)該函數(shù)返回到其調(diào)用者時涡尘,將自動釋放內(nèi)存
i32 - 32位4字節(jié)的整數(shù)
align - 對齊
load - 讀出,store 寫入
icmp - 兩個整數(shù)值比較响迂,返回布爾值
br - 選擇分支考抄,根據(jù)條件來轉(zhuǎn)向label,不根據(jù)條件跳轉(zhuǎn)的話類似 goto
label - 代碼標(biāo)簽
call - 調(diào)用函數(shù)
具體可以參考官方文檔:https://llvm.org/docs/LangRef.html