iOS-探究Runtime

前言:本文探究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結(jié)構(gòu)

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

class_rw_t結(jié)構(gòu)

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

class_ro_t結(jié)構(gòu)

第二部分: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ù)

函數(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的指令准夷,可以將具體的類型表示成字符串編碼钥飞,如下:

TypeEncoding編碼表
TypeEncoding編碼表

Q:什么是方法緩存?

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

cache_t結(jié)構(gòu)

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ā)送

消息發(fā)送執(zhí)行流程
  • receiver通過isa指針找到receiverClass
  • receiverClass通過superclass指針找到superClass
  • 如果是從class_rw_t中查找方法
    1.已經(jīng)排序的匪凡,二分查找
    2.沒有排序的膊畴,遍歷查找

3.2 動態(tài)解析

動態(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ā)執(zhí)行流程

消息轉(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_super2SEL

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蔗彤,一起剝皮案震驚了整個濱河市川梅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌然遏,老刑警劉巖贫途,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異待侵,居然都是意外死亡丢早,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門秧倾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怨酝,“玉大人,你說我怎么就攤上這事那先∨┾” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵售淡,是天一觀的道長斤葱。 經(jīng)常有香客問我,道長揖闸,這世上最難降的妖魔是什么揍堕? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮楔壤,結(jié)果婚禮上鹤啡,老公的妹妹穿的比我還像新娘。我一直安慰自己蹲嚣,他們只是感情好递瑰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布祟牲。 她就那樣靜靜地躺著,像睡著了一般抖部。 火紅的嫁衣襯著肌膚如雪说贝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天慎颗,我揣著相機(jī)與錄音乡恕,去河邊找鬼。 笑死俯萎,一個胖子當(dāng)著我的面吹牛傲宜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播夫啊,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼函卒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了撇眯?” 一聲冷哼從身側(cè)響起报嵌,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎熊榛,沒想到半個月后锚国,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玄坦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年血筑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片营搅。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡云挟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出转质,到底是詐尸還是另有隱情园欣,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布休蟹,位于F島的核電站沸枯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏赂弓。R本人自食惡果不足惜绑榴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盈魁。 院中可真熱鬧翔怎,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至容握,卻和暖如春宣脉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剔氏。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工塑猖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人谈跛。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓羊苟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親币旧。 傳聞我的和親對象是個殘疾皇子践险,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354