文章分享至我的个人技术博客: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
在目录中, 我并没有放到工程里.