玩轉iOS開發:iOS開發中的裝逼技術 - RunTime(一)

文章分享至個人我的技術博客:https://cainluo.github.io/15033286127687.htmlhtml


RunTimeObjective-C的特性, 若是用別的話來講, 就是由於Objective-C是動態語言, 而後RunTime就是它的運行時機制這些這些, 而後就沒而後了...git

可是對於我這些渣渣來講, 我的認爲就是一堆C語言寫的東西, 廢話少說了, 直接來擼吧.github

轉載聲明:如須要轉載該文章, 請聯繫做者, 而且註明出處, 以及不能擅自修改本文.web


objc_msgSend

在咱們日常的使用當中, 會常常聲明一個函數, 而後去調用, 但裏面作了什麼操做, 咱們並不知道, 如今咱們來看一段代碼:vim

#import "RunTimeModel.h"
#import <objc/message.h>
#import <objc/objc.h>

@implementation RunTimeModel

- (instancetype)init {
    self = [super init];
    
    if (self) {
        
        [self sendMessage];
        [self sendMessage:100];
    }
    
    return self;
}


- (void)sendMessage {
    
    NSLog(@"Message");
}

- (void)sendMessage:(NSInteger)messageCount {
    
    NSLog(@"Message: %ld", messageCount);
}

@end
複製代碼

這段代碼, 是咱們正常寫的Objective-C代碼, 咱們能夠經過終端的命令行, 進行重編:緩存

clang -rewrite-objc RunTimeModel.m
複製代碼

1

而後就會獲得一個RunTimeModel.cpp的文件, 裏面有90000+行代碼, 這裏面咱們要找到一段東西:bash

static instancetype _I_RunTimeModel_init(RunTimeModel * self, SEL _cmd) {
    self = ((RunTimeModel *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("RunTimeModel"))}, sel_registerName("init"));

    if (self) {

        ((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage"));
        ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage:"), (NSInteger)100);
    }

    return self;
}
複製代碼
((void (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage"));

((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("sendMessage:"), (NSInteger)100);
複製代碼

2

這就是咱們在.m文件裏調用方法時所進行的操做, 會轉化成消息發送的形式進行通訊, objc_msgSend是在#import <objc/message.h>文件中, 聲明方式:微信

OBJC_EXPORT void objc_msgSend(void /* id self, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
複製代碼

3

這裏是有兩個基礎參數, 分別是idSEL.app


id / SEL

idSEL都是定義在#include <objc/objc.h>中:ide

typedef struct objc_object *id;

typedef struct objc_selector *SEL;
複製代碼
  • SEL: 本質就是一個映射到方法的C字符串, 咱們能夠用Objective-C@selector()或者RunTime裏的sel_registerName來獲取一個SEL類型的方法選擇器.
  • id: 它是一個結構體指針類型, 能夠指向Objective-C中的任何對象.

objc_object定義:

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};
複製代碼

4

其實這纔是對象原本的面貌, 不要給漂亮的外表給矇騙了咯.

這個結構體就只有一個isa成員變量, 對象是能夠經過isa指針找到本身所屬的類, 看到這裏, 咱們就不由疑惑, isa是一個Class的成員變量, 那Class又是啥?


Class

咱們在#include <objc/objc.h>中實際上是有看到Class的聲明:

typedef struct objc_class *Class;
複製代碼

但實際上Class是定義在#include <objc/runtime.h>中:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
複製代碼

5

這裏解釋一下里面的東東:

  • Class: 也有一個isa指針, 指向所屬的meta(元類).
  • super_class: 指向的是它的超類.
  • name: 類名.
  • version: 類的版本信息.
  • info: 類的詳情信息.
  • instance_size: 這個類的示例對象的大小.
  • ivars: 指向這個類的成員變量列表, 包括內部的變量.
  • methodLists: 指向這個類的示例方法列表, 它將方法選擇器和方法實現地址聯繫在一塊兒.
  • cache: Runtime會把被調用的方法存到cache中, 下次查找的時候效率更高, 其實就是這個方法第一次被調用了以後, 爲了之後還會被調用的可能而作的緩存.
  • protocols: 指向這個類的協議列表.

這裏的methodLists須要注意一下, 它是指向objc_method_list指針的指針, 也就是說能夠動態修改methodLists的值來添加成員方法, 咱們常常用的Category就是醬紫來的, 也由於這個東西, Category通常是沒辦法添加屬性, 須要咱們本身寫寫寫.

看到這裏, 基本的東西咱們都差很少了解完了, 如今加個補刀, 看看整個運行的過程:

  • Runtime會把咱們的方法調用轉化爲消息發送, 也就是咱們剛剛說的objc_msgSend, 而且把方法的調用者和方法選擇器, 當作參數傳遞過去.
  • 這個時候方法的調用者會經過isa指針來找到方法所屬的類, 而後在cache或者methodLists查找被調用的方法, 找到了就跳轉到對應的方法去執行.
    • 若是在類中沒有找到該方法, 就會經過super_class往更上一級的超類中查找, 查找到了就執行(若是找不到呢? 這個後面會有補充).

說完這裏, 有些人確定會很奇怪, 這裏的methodLists裝的是實例方法, 那類方法呢?

其實, 類方法是被存儲在元類中, Class會經過isa指針找到所屬的元類, 這些類方法就是存在這裏了, 具體怎麼獲取類方法, 咱們能夠看看代碼:

- (void)getClassMethods {
    
    NSObject *obj = [[NSObject alloc] init];
    
    unsigned int methodCount = 0;
    
    const char *className = class_getName([obj class]);
    
    Class metaClass = objc_getMetaClass(className);
    
    Method *methodList = class_copyMethodList(metaClass, &methodCount);
    
    for (int i = 0; i < methodCount; i++) {
        
        Method method = methodList[i];
        
        SEL selector = method_getName(method);
        
        const char *methodName = sel_getName(selector);
        
        NSLog(@"%s", methodName);
    }
}
複製代碼

打印出來的結果:vim

2017-08-22 13:24:19.455 1.RunTime[32885:2667202] _installAppearanceSwizzlesForSetter:
2017-08-22 13:24:19.456 1.RunTime[32885:2667202] __accessibilityGuidedAccessStateEnabled
2017-08-22 13:24:19.456 1.RunTime[32885:2667202] __accessibilityGuidedAccessRestrictionStateForIdentifier:
2017-08-22 13:24:19.456 1.RunTime[32885:2667202] __accessibilityRequestGuidedAccessSession:completion:
2017-08-22 13:24:19.456 1.RunTime[32885:2667202] isSelectorExcludedFromWebScript:
2017-08-22 13:24:19.457 1.RunTime[32885:2667202] isKeyExcludedFromWebScript:
2017-08-22 13:24:19.457 1.RunTime[32885:2667202] _webkit_invokeOnMainThread
2017-08-22 13:24:19.457 1.RunTime[32885:2667202] sbs_dataFromObject:
2017-08-22 13:24:19.457 1.RunTime[32885:2667202] sbs_objectFromData:
2017-08-22 13:24:19.458 1.RunTime[32885:2667202] sbs_dataWithValue:
2017-08-22 13:24:19.458 1.RunTime[32885:2667202] sbs_valueFromData:ofType:
2017-08-22 13:24:19.458 1.RunTime[32885:2667202] CA_automaticallyNotifiesObservers:
2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_setterForProperty:
2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_getterForProperty:
2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_encodesPropertyConditionally:type:
2017-08-22 13:24:19.459 1.RunTime[32885:2667202] CA_CAMLPropertyForKey:
2017-08-22 13:24:19.459 1.RunTime[32885:2667202] bs_decodedFromData:
2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_objectFromData:
2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_secureObjectFromData:ofClass:
2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_secureObjectFromData:ofClasses:
2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_synchronousWrapper:timeout:
2017-08-22 13:24:19.460 1.RunTime[32885:2667202] bs_secureDataFromObject:
2017-08-22 13:24:19.461 1.RunTime[32885:2667202] bs_dataFromObject:
2017-08-22 13:24:19.461 1.RunTime[32885:2667202] bs_secureDecodedFromData:withAdditionalClasses:
2017-08-22 13:24:19.461 1.RunTime[32885:2667202] bs_secureDecodedFromData:
2017-08-22 13:24:19.506 1.RunTime[32885:2667202] replacementObjectForPortCoder:
2017-08-22 13:24:19.506 1.RunTime[32885:2667202] instanceMethodDescriptionForSelector:
2017-08-22 13:24:19.507 1.RunTime[32885:2667202] methodDescriptionForSelector:
2017-08-22 13:24:19.507 1.RunTime[32885:2667202] _localClassNameForClass
2017-08-22 13:24:19.507 1.RunTime[32885:2667202] cancelPreviousPerformRequestsWithTarget:selector:object:
2017-08-22 13:24:19.507 1.RunTime[32885:2667202] cancelPreviousPerformRequestsWithTarget:
2017-08-22 13:24:19.507 1.RunTime[32885:2667202] setVersion:
2017-08-22 13:24:19.508 1.RunTime[32885:2667202] implementsSelector:
2017-08-22 13:24:19.508 1.RunTime[32885:2667202] instancesImplementSelector:
2017-08-22 13:24:19.508 1.RunTime[32885:2667202] load
2017-08-22 13:24:19.508 1.RunTime[32885:2667202] version
2017-08-22 13:24:19.509 1.RunTime[32885:2667202] classForKeyedUnarchiver
2017-08-22 13:24:19.509 1.RunTime[32885:2667202] classFallbacksForKeyedArchiver
2017-08-22 13:24:19.509 1.RunTime[32885:2667202] _shouldAddObservationForwardersForKey:
2017-08-22 13:24:19.509 1.RunTime[32885:2667202] setKeys:triggerChangeNotificationsForDependentKey:
2017-08-22 13:24:19.510 1.RunTime[32885:2667202] automaticallyNotifiesObserversForKey:
2017-08-22 13:24:19.510 1.RunTime[32885:2667202] _keysForValuesAffectingValueForKey:
2017-08-22 13:24:19.510 1.RunTime[32885:2667202] keyPathsForValuesAffectingValueForKey:
2017-08-22 13:24:19.510 1.RunTime[32885:2667202] _createValueGetterWithContainerClassID:key:
2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createValueSetterWithContainerClassID:key:
2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createMutableOrderedSetValueGetterWithContainerClassID:key:
2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createMutableSetValueGetterWithContainerClassID:key:
2017-08-22 13:24:19.511 1.RunTime[32885:2667202] _createValuePrimitiveGetterWithContainerClassID:key:
2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createValuePrimitiveSetterWithContainerClassID:key:
2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createOtherValueGetterWithContainerClassID:key:
2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createOtherValueSetterWithContainerClassID:key:
2017-08-22 13:24:19.512 1.RunTime[32885:2667202] _createMutableArrayValueGetterWithContainerClassID:key:
2017-08-22 13:24:19.513 1.RunTime[32885:2667202] accessInstanceVariablesDirectly
2017-08-22 13:24:19.513 1.RunTime[32885:2667202] instanceMethodSignatureForSelector:
2017-08-22 13:24:19.513 1.RunTime[32885:2667202] load
2017-08-22 13:24:19.513 1.RunTime[32885:2667202] dealloc
2017-08-22 13:24:19.514 1.RunTime[32885:2667202] doesNotRecognizeSelector:
2017-08-22 13:24:19.514 1.RunTime[32885:2667202] description
2017-08-22 13:24:19.514 1.RunTime[32885:2667202] methodSignatureForSelector:
2017-08-22 13:24:19.514 1.RunTime[32885:2667202] __allocWithZone_OA:
2017-08-22 13:24:19.515 1.RunTime[32885:2667202] _copyDescription
2017-08-22 13:24:19.515 1.RunTime[32885:2667202] init
2017-08-22 13:24:19.515 1.RunTime[32885:2667202] zone
2017-08-22 13:24:19.515 1.RunTime[32885:2667202] instancesRespondToSelector:
2017-08-22 13:24:19.516 1.RunTime[32885:2667202] instanceMethodForSelector:
2017-08-22 13:24:19.516 1.RunTime[32885:2667202] isAncestorOfObject:
2017-08-22 13:24:19.516 1.RunTime[32885:2667202] instanceMethodSignatureForSelector:
2017-08-22 13:24:19.516 1.RunTime[32885:2667202] load
2017-08-22 13:24:19.517 1.RunTime[32885:2667202] initialize
2017-08-22 13:24:19.517 1.RunTime[32885:2667202] resolveInstanceMethod:
2017-08-22 13:24:19.517 1.RunTime[32885:2667202] resolveClassMethod:
2017-08-22 13:24:19.517 1.RunTime[32885:2667202] retain
2017-08-22 13:24:19.518 1.RunTime[32885:2667202] release
2017-08-22 13:24:19.518 1.RunTime[32885:2667202] autorelease
2017-08-22 13:24:19.518 1.RunTime[32885:2667202] retainCount
2017-08-22 13:24:19.518 1.RunTime[32885:2667202] alloc
2017-08-22 13:24:19.519 1.RunTime[32885:2667202] allocWithZone:
2017-08-22 13:24:19.519 1.RunTime[32885:2667202] dealloc
2017-08-22 13:24:19.519 1.RunTime[32885:2667202] copy
2017-08-22 13:24:19.519 1.RunTime[32885:2667202] new
2017-08-22 13:24:19.520 1.RunTime[32885:2667202] forwardInvocation:
2017-08-22 13:24:19.520 1.RunTime[32885:2667202] _tryRetain
2017-08-22 13:24:19.520 1.RunTime[32885:2667202] _isDeallocating
2017-08-22 13:24:19.520 1.RunTime[32885:2667202] retainWeakReference
2017-08-22 13:24:19.521 1.RunTime[32885:2667202] allowsWeakReference
2017-08-22 13:24:19.521 1.RunTime[32885:2667202] copyWithZone:
2017-08-22 13:24:19.521 1.RunTime[32885:2667202] mutableCopyWithZone:
2017-08-22 13:24:19.522 1.RunTime[32885:2667202] doesNotRecognizeSelector:
2017-08-22 13:24:19.522 1.RunTime[32885:2667202] description
2017-08-22 13:24:19.522 1.RunTime[32885:2667202] isFault
2017-08-22 13:24:19.522 1.RunTime[32885:2667202] mutableCopy
2017-08-22 13:24:19.523 1.RunTime[32885:2667202] performSelector:withObject:
2017-08-22 13:24:19.523 1.RunTime[32885:2667202] isMemberOfClass:
2017-08-22 13:24:19.524 1.RunTime[32885:2667202] hash
2017-08-22 13:24:19.524 1.RunTime[32885:2667202] isEqual:
2017-08-22 13:24:19.524 1.RunTime[32885:2667202] self
2017-08-22 13:24:19.524 1.RunTime[32885:2667202] performSelector:
2017-08-22 13:24:19.525 1.RunTime[32885:2667202] conformsToProtocol:
2017-08-22 13:24:19.525 1.RunTime[32885:2667202] methodSignatureForSelector:
2017-08-22 13:24:19.525 1.RunTime[32885:2667202] forwardingTargetForSelector:
2017-08-22 13:24:19.525 1.RunTime[32885:2667202] methodForSelector:
2017-08-22 13:24:19.526 1.RunTime[32885:2667202] performSelector:withObject:withObject:
2017-08-22 13:24:19.526 1.RunTime[32885:2667202] superclass
2017-08-22 13:24:19.526 1.RunTime[32885:2667202] isSubclassOfClass:
2017-08-22 13:24:19.527 1.RunTime[32885:2667202] class
2017-08-22 13:24:19.527 1.RunTime[32885:2667202] init
2017-08-22 13:24:19.528 1.RunTime[32885:2667202] debugDescription
2017-08-22 13:24:19.528 1.RunTime[32885:2667202] isProxy
2017-08-22 13:24:19.529 1.RunTime[32885:2667202] respondsToSelector:
2017-08-22 13:24:19.529 1.RunTime[32885:2667202] isKindOfClass:
複製代碼

isa的補充

這裏順帶補充一下isa指針的指向:

  • isa指針指向的是元類.
  • 元類isa指針指向的是根類.
  • 若是根類或者是元類的超類是NSObject, 那麼就是指向本身.
  • NSObject是沒有超類的.

6


工程地址

項目地址: https://github.com/CainRun/iOS-Project-Example/tree/master/RunTime/玩轉iOS開發:iOS中的RunTime(一)

注意: RunTimeModel.cpp在目錄中, 我並無放到工程裏.


最後

碼字很費腦, 看官賞點飯錢可好

微信

支付寶
相關文章
相關標籤/搜索