Objective-C作為一門(mén)高級(jí)編程語(yǔ)言,想要成為可執(zhí)行文件需要先編譯成匯編語(yǔ)言,在匯編成機(jī)器語(yǔ)言,機(jī)器語(yǔ)言也是計(jì)算機(jī)能識(shí)別的唯一語(yǔ)言。但是Objective-C并不能直接編譯成匯編語(yǔ)言,需要先轉(zhuǎn)寫(xiě)為C語(yǔ)言在進(jìn)行編譯和匯編的操作。從Objective-C到C語(yǔ)言的過(guò)渡就是由Runtime來(lái)實(shí)現(xiàn)的。
Objective-C的語(yǔ)言特性是動(dòng)態(tài)性比較強(qiáng),這種動(dòng)態(tài)性就是由Runtime API來(lái)支撐的。想要了解Runtime的原理,首先需要知道OC語(yǔ)言的isa、Cache緩存、class_rw_t。
isa詳解
要想學(xué)習(xí)Runtime,首先要了解它底層的一些常用的數(shù)據(jù)結(jié)構(gòu),比如isa指針。
在arm64架構(gòu)之前,isa就是一個(gè)普通的指針,存儲(chǔ)著Class、Meta-Class對(duì)象的內(nèi)存地址。
從arm64架構(gòu)開(kāi)始,對(duì)isa進(jìn)行了優(yōu)化,變成了一個(gè)共用體(union)結(jié)構(gòu),還使用位域來(lái)存儲(chǔ)更多的信息。共用體如下
union isa_t
{
Class cls;
uintptr_t bits;
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;
};
我們來(lái)看一段代碼
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property (nonatomic, assign, getter=isTall) BOOL tall;
@property (nonatomic, assign, getter=isRich) BOOL rich;
@property (nonatomic, assign, getter=isHandsome) BOOL handsome;
@end
#import "Person.h"
@implementation Person
@end
#import <Foundation/Foundation.h>
#import "Person.h"
#import <objc/runtime.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
person.rich = NO;
person.tall = YES;
person.handsome = NO;
NSLog(@"%zd",class_getInstanceSize([Person class]));
}
return 0;
}
在main.m函數(shù)中,系統(tǒng)給Person對(duì)象分配了16個(gè)字節(jié)的內(nèi)存,其中Person的isa指針占用8個(gè)字節(jié),三個(gè)屬性占用3個(gè)字節(jié)。
既然Person的屬性是BOOL類(lèi)型,可以考慮使用共用體。
共用體:共用體內(nèi)的成員變量共用一塊內(nèi)存。
則Person類(lèi)的.m文件內(nèi),可以修改成如下:
#import "Person.h"
@interface Person()
{
union{
char bits;
struct{
char tall : 1;
char rich : 1;
char handsome : 1;
};
}_tallRichHandsome;
}
@end
@implementation Person
- (void)setTall:(BOOL)tall{
_tallRichHandsome.bits = tall;
}
- (BOOL)isTall{
return !!(_tallRichHandsome.bits );
}
- (void)setRich:(BOOL)rich{
_tallRichHandsome.bits = rich;
}
- (BOOL)isRich{
return !!(_tallRichHandsome.bits);
}
- (void)setHandsome:(BOOL)handsome{
_tallRichHandsome.bits = handsome;
}
- (BOOL)isHandsome{
return !!( _tallRichHandsome.bits);
}
@end
在結(jié)構(gòu)體union中
struct{
char tall : 1;
char rich : 1;
char handsome : 1;
};
只是為了方便閱讀代碼,struct內(nèi)的成員變量,都存儲(chǔ)在union的char中。所以回到開(kāi)頭的union isa_t中,struct是為了閱讀方便,共用體union中的信息都存儲(chǔ)在uintptr_t bits;中。
Class的結(jié)構(gòu)

-
class_rw_t里面的methods、properties、protocols是二維數(shù)組,是可讀可寫(xiě)的,包含了類(lèi)的初始內(nèi)容、分類(lèi)的內(nèi)容。
class_rw_t方法結(jié)構(gòu)圖
method_array_t是一個(gè)二維數(shù)組,每一個(gè)元素是一個(gè)分類(lèi)或者類(lèi)的方法數(shù)組method_list_t。在method_list_t數(shù)組中包含的才是分類(lèi)或者類(lèi)的方法信息。 -
class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一維數(shù)組,是只讀的,包含了類(lèi)的初始內(nèi)容。
class_ro_t結(jié)構(gòu)圖 -
method_t是對(duì)方法\函數(shù)的封裝
method_t -
IMP代表函數(shù)的具體實(shí)現(xiàn)
IMP -
SEL代表方法\函數(shù)名,一般叫做選擇器。底層結(jié)構(gòu)跟char *類(lèi)似
可以通過(guò)@selector()和sel_registerName()獲得。
方法緩存
Class內(nèi)部結(jié)構(gòu)中有個(gè)方法緩存(cache_t),用散列表來(lái)緩存曾經(jīng)調(diào)用的方法,可以提高方法的查找速度。

buckets是散列表,是數(shù)組。
- 散列表的存儲(chǔ)方式
散列表的存儲(chǔ)方式
在上圖中,當(dāng)Person類(lèi)中有方法- (void)personTest; - 首先,通過(guò)
@selector(personTest)&_Mask,得出一個(gè)值,比如得到2,則將這個(gè)方法personTest存儲(chǔ)到下表為2的數(shù)組中。一般存儲(chǔ)的位置從高到低。如果當(dāng)前被存儲(chǔ)內(nèi)容,這得到數(shù)值減1,往下存儲(chǔ)。 - 當(dāng)調(diào)用這個(gè)方法時(shí),通過(guò)
@selector(personTest)&_Mask,得到同樣的值,然后直接找到這個(gè)方法的地址值,直接調(diào)用。如果取值時(shí),當(dāng)前的cache_key_t不是所需要的,這得到數(shù)值減1,往下取值。 - 缺點(diǎn):用空間換時(shí)間。
objc_msgSend
先來(lái)看段代碼
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)personTest;
@end
#import "Person.h"
@implementation Person
- (void)personTest{
}
@end
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *person = [[Person alloc] init];
[person personTest];
}
return 0;
}
當(dāng)調(diào)用- (void)personTest;方法時(shí),是在底層轉(zhuǎn)化為C語(yǔ)言調(diào)用
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("personTest"));
}
return 0;
}
- OC中的方法調(diào)用,其實(shí)都是轉(zhuǎn)化為
objc_msgSend函數(shù)的調(diào)用,給receiver(方法接收者)發(fā)送了一條消息(selector方法名); -
objc_msgSend的執(zhí)行流程可以分為3大階段:
?? 消息發(fā)送
- 1、判斷消息接收者
receiver是否為nil。如果是nil,這直接退出; - 2、如果
receiver不為nil,receiver通過(guò)isa指針找到receiverClass(接收者類(lèi)對(duì)象)。從receiverClass的cache中查找方法。如果找到方法,調(diào)用方法,結(jié)束查找; - 3、如果沒(méi)有找到方法,則從
reveiverClass的class_rw_t中查找方法。如果找到了方法,調(diào)用方法,結(jié)束查找,并將方法緩存到receiverClass的cache中; - 4、如果沒(méi)找到方法,從superClass的cache中查找方法,如果找到方法,調(diào)用方法,結(jié)束查找,并將方法緩存到
receiverClass的cache中; - 5、如果沒(méi)有找到方法,從superClass的
class_rw_t中查找方法。如果找到方法,調(diào)用方法,結(jié)束查找,并將方法緩存到receiverClass的cache中; - 6、如果沒(méi)找到方法,則判斷是否還有superclass。如果有,則執(zhí)行第4步。如果沒(méi)找到方法,則動(dòng)態(tài)方法解析。
??動(dòng)態(tài)方法解析
- 是否曾經(jīng)存在過(guò)動(dòng)態(tài)方法解析,如果是,就進(jìn)入消息轉(zhuǎn)發(fā)階段。
- 如果沒(méi)有動(dòng)態(tài)方法解析過(guò),則調(diào)用
+ (BOOL)resolveInstanceMethod:(SEL)sel方法(實(shí)例方法調(diào)用)或者+(BOOL)resolveClassMethod:(SEL)sel(類(lèi)方法調(diào)用)判斷是否有動(dòng)態(tài)綁定方法,并標(biāo)記為已經(jīng)動(dòng)態(tài)方法解析,然后進(jìn)入消息發(fā)送階段。
??消息轉(zhuǎn)發(fā)
- 通過(guò)動(dòng)態(tài)方法解析不成功,則調(diào)用
- (id)forwardingTargetForSelector:(SEL)aSelector方法,把這條消息轉(zhuǎn)給其他接收者來(lái)處理,如果有其他接收者來(lái)處理,返回不為nil,則調(diào)用objc_msgSend(返回值,SEL); - 如果返回值為nil,則調(diào)用
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法,生成方法簽名,然后系統(tǒng)用這個(gè)方法簽名生成NSInvocation對(duì)象,如果這個(gè)方法返回的nil,則崩潰報(bào)錯(cuò)unrecognized selector sent to instance; - 如果
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法返回值不為nil,則調(diào)用- (void)forwardInvocation:(NSInvocation *)anInvocation方法。
注意:
1、如果調(diào)用的是類(lèi)方法,上述流程中,方法換成類(lèi)方法(+開(kāi)頭)。
2、dynamic告訴編譯器不要自動(dòng)生成setter方法和getter方法的實(shí)現(xiàn),等到運(yùn)行時(shí)在添加方法的實(shí)現(xiàn)。
3、@synthesize age = _age;是為age屬性生成_age,并且自動(dòng)生成setter方法和getter方法,并賦值。
有關(guān)super
[super message]的底層實(shí)現(xiàn)
objc_msgSendSuper(self,[Person class],@selector(message));
[super class]返回的是當(dāng)前類(lèi)
- (class)class{
return object_getClass(self);
}
[super superClass]返回的是父類(lèi)
- (Class)superclass{
return class_getSuperclass(object_getClass(self));
}
- 1、消息接收者仍然是子類(lèi)對(duì)象;
- 2、從父類(lèi)開(kāi)始查找方法的實(shí)現(xiàn);
Runtime的應(yīng)用API
類(lèi)相關(guān)
- 1、動(dòng)態(tài)創(chuàng)建一個(gè)類(lèi)(參數(shù):父類(lèi),類(lèi)名,額外的內(nèi)存空間)創(chuàng)建之后要注冊(cè)這個(gè)類(lèi)
Class objc_allocateClassPair(Class superClass,const char *name,size_t extraBytes) - 2、注冊(cè)一個(gè)類(lèi)(要在類(lèi)注冊(cè)之前添加成員變量)
Void objc_registerClassPair(Class cls) - 3、銷(xiāo)毀一個(gè)類(lèi)
Void objc_disposeClassPair(Class cls) - 4、獲取isa指向的Class,或者類(lèi)對(duì)象(元類(lèi)對(duì)象)
Class object_getClass(id obj) - 5、設(shè)置isa指向的class,此isa指向其他的類(lèi)
Class object_setClass(id obj,class cls) - 6、判斷一個(gè)OC對(duì)象是否為class,傳入實(shí)例對(duì)象,BOOL為0,傳入類(lèi)對(duì)象,BOOL為1
BOOL object_isClass(id obj) - 7、判斷一個(gè)Class是否為元素
BOOL class_isMetaClass(Class cls) - 8、獲取父類(lèi)
Class class_getSuperclass(class cls)
成員變量相關(guān)
- 9、獲取成員變量信息(獲取不到值)
Ivar class_getInstanceVariable(Class cls,const char *name) - 10、拷貝實(shí)例變量列表(最后需要調(diào)用free釋放)
Ivar *class_copyIvarList(Class cls,unsigned int *outCount) - 11、設(shè)置和獲取成員變量的值
Void object_setIvar(id obj,Ivar ivar,id value)
Id object_getIvar(id obj,Ivar ivar) - 12、動(dòng)態(tài)添加成員變量
BOOL class_addIvar(Class cls,const char *name,size_t size,unit8_t alignment,
const char *types) - 13、獲取成員變量的相關(guān)信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
屬性相關(guān)
- 14、獲取一個(gè)屬性
- 15、拷貝屬性列表(最后需要調(diào)用free釋放)
objc_property_t *class_copyPropertyList(Class cls,unsigned int *outCount) - 16、動(dòng)態(tài)添加屬性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t
*attributes, unsigned int attributeCount) - 17、動(dòng)態(tài)替換屬性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t
*attributes,unsigned int attributeCount) - 18、獲取屬性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
方法相關(guān)
- 19、獲得一個(gè)實(shí)例方法、類(lèi)方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name) - 20、方法實(shí)現(xiàn)相關(guān)操作
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2) - 21、拷貝方法列表(最后需要調(diào)用free釋放)
Method *class_copyMethodList(Class cls, unsigned int *outCount) - 22、動(dòng)態(tài)添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) - 23、動(dòng)態(tài)替換方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) - 24、獲取方法的相關(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) - 25、選擇器相關(guān)
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str) - 26、用block作為方法實(shí)現(xiàn)
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)




