iOS-探究Runtime

前言:本文探究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結(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:簡(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ù)

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

TypeEncoding編碼表
TypeEncoding編碼表

Q:什么是方法緩存?

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

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

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

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

3.2 動(dòng)態(tài)解析

動(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ā)執(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 指派其他對(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_super2SEL

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

最后編輯于
?著作權(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ù)。

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