文章分享至我的個人技術部落格:https://cainluo.github.io/15033286127687.html
RunTime
是Objective-C
的特性, 如果用別的話來說, 就是因為Objective-C
是動態語言, 然後RunTime
就是它的執行時機制這些這些, 然後就沒然後了…
但是對於我這些渣渣來說, 個人認為就是一堆C
語言寫的東西, 廢話少說了, 直接來擼吧.
轉載宣告:如需要轉載該文章, 請聯絡作者, 並且註明出處, 以及不能擅自修改本文.
objc_msgSend
在我們平常的使用當中, 會經常宣告一個函式, 然後去呼叫, 但裡面做了什麼操作, 我們並不知道, 現在我們來看一段程式碼:
#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
複製程式碼
然後就會得到一個RunTimeModel.cpp
的檔案, 裡面有90000+
行程式碼, 這裡面我們要找到一段東西:
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);
複製程式碼
這就是我們在.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);
複製程式碼
這裡是有兩個基礎引數, 分別是id
和SEL
.
id / SEL
id
和SEL
都是定義在#include <objc/objc.h>
中:
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;
};
複製程式碼
其實這才是物件本來的面貌, 不要給漂亮的外表給矇騙了咯.
這個結構體就只有一個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;
複製程式碼
這裡解釋一下里面的東東:
- 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
是沒有超類的.
工程地址
專案地址: https://github.com/CainRun/iOS-Project-Example/tree/master/RunTime/玩轉iOS開發:iOS中的RunTime(一)
注意: RunTimeModel.cpp
在目錄中, 我並沒有放到工程裡.