文章分享至我的个人技术博客:https://cainluo.github.io/15033286127687.html
RunTime
是Objective-C
的特性, 如果用别的话来说, 就是因为Objective-C
是动态语言, 然后RunTime
就是它的运行时机制这些这些, 然后就没然后了...
但是对于我这些渣渣来说, 个人认为就是一堆C
语言写的东西, 废话少说了, 直接来撸吧.
转载声明:如需要转载该文章, 请联系作者, 并且注明出处, 以及不能擅自修改本文.
objc_msgSend
在我们平常的使用当中, 会经常声明一个函数, 然后去调用, 但里面做了什么操作, 我们并不知道, 现在我们来看一段代码:
#import "RunTimeModel.h"#import#import @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] __accessibilityGuidedAccessStateEnabled2017-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_invokeOnMainThread2017-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] _localClassNameForClass2017-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] load2017-08-22 13:24:19.508 1.RunTime[32885:2667202] version2017-08-22 13:24:19.509 1.RunTime[32885:2667202] classForKeyedUnarchiver2017-08-22 13:24:19.509 1.RunTime[32885:2667202] classFallbacksForKeyedArchiver2017-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] accessInstanceVariablesDirectly2017-08-22 13:24:19.513 1.RunTime[32885:2667202] instanceMethodSignatureForSelector:2017-08-22 13:24:19.513 1.RunTime[32885:2667202] load2017-08-22 13:24:19.513 1.RunTime[32885:2667202] dealloc2017-08-22 13:24:19.514 1.RunTime[32885:2667202] doesNotRecognizeSelector:2017-08-22 13:24:19.514 1.RunTime[32885:2667202] description2017-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] _copyDescription2017-08-22 13:24:19.515 1.RunTime[32885:2667202] init2017-08-22 13:24:19.515 1.RunTime[32885:2667202] zone2017-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] load2017-08-22 13:24:19.517 1.RunTime[32885:2667202] initialize2017-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] retain2017-08-22 13:24:19.518 1.RunTime[32885:2667202] release2017-08-22 13:24:19.518 1.RunTime[32885:2667202] autorelease2017-08-22 13:24:19.518 1.RunTime[32885:2667202] retainCount2017-08-22 13:24:19.518 1.RunTime[32885:2667202] alloc2017-08-22 13:24:19.519 1.RunTime[32885:2667202] allocWithZone:2017-08-22 13:24:19.519 1.RunTime[32885:2667202] dealloc2017-08-22 13:24:19.519 1.RunTime[32885:2667202] copy2017-08-22 13:24:19.519 1.RunTime[32885:2667202] new2017-08-22 13:24:19.520 1.RunTime[32885:2667202] forwardInvocation:2017-08-22 13:24:19.520 1.RunTime[32885:2667202] _tryRetain2017-08-22 13:24:19.520 1.RunTime[32885:2667202] _isDeallocating2017-08-22 13:24:19.520 1.RunTime[32885:2667202] retainWeakReference2017-08-22 13:24:19.521 1.RunTime[32885:2667202] allowsWeakReference2017-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] description2017-08-22 13:24:19.522 1.RunTime[32885:2667202] isFault2017-08-22 13:24:19.522 1.RunTime[32885:2667202] mutableCopy2017-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] hash2017-08-22 13:24:19.524 1.RunTime[32885:2667202] isEqual:2017-08-22 13:24:19.524 1.RunTime[32885:2667202] self2017-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] superclass2017-08-22 13:24:19.526 1.RunTime[32885:2667202] isSubclassOfClass:2017-08-22 13:24:19.527 1.RunTime[32885:2667202] class2017-08-22 13:24:19.527 1.RunTime[32885:2667202] init2017-08-22 13:24:19.528 1.RunTime[32885:2667202] debugDescription2017-08-22 13:24:19.528 1.RunTime[32885:2667202] isProxy2017-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
在目录中, 我并没有放到工程里.