總述
運行時(runtime)是一種面向?qū)ο蟮木幊陶Z言的運行環(huán)境.
運行時(runtime)是Objective-C的核心, Objective-C就是基于運行時(runtime)的.
Objective-C是基于C語言加入了面向?qū)ο筇匦院拖⑥D(zhuǎn)發(fā)機(jī)制的動態(tài)語言.
Objective-C需要一個編譯器浓领,還需要Runtime系統(tǒng)來動態(tài)創(chuàng)建類和對象,進(jìn)行消息發(fā)送和轉(zhuǎn)發(fā)范嘱。
Objective-C最主要的特點就是在程序運行時, 以發(fā)送消息的方式調(diào)用方法.
C語言的函數(shù)調(diào)用方式是使用靜態(tài)綁定(static binding).在編譯期就能決定運行時所應(yīng)調(diào)用的函數(shù).
在Objective-C中,如果向某對象傳遞消息赎懦,那就會使用動態(tài)綁定機(jī)制來決定需要調(diào)用的方法良风。在底層,所有方法都是普通的C語言函數(shù)梅猿。
頭文件
import <objc/runtime.h>
import <objc/message.h>
OC方法調(diào)用
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self testDemo:nil];
}
- (void)testDemo:(id)param {
NSLog(@"%s",__func__);
}
分析[self testDemo];
1.self 叫做 接收者(receiver)
2.testDemo 叫做 選擇子(selector)
3.選擇子與參數(shù)合起來稱為 消息(message)
4.編譯器看到此消息后鞋邑,將其轉(zhuǎn)換為一條標(biāo)準(zhǔn)的C語言函數(shù)調(diào)用
5.所調(diào)用的函數(shù)是消息傳遞機(jī)制中的核心函數(shù)诵次,叫做objc_msgSend()
運行時(runtime)消息發(fā)送 == OC方法調(diào)用底層實現(xiàn)
運行時(runtime)消息發(fā)送函數(shù):OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0);
OBJC2_UNAVAILABLE是一個Apple對Objc系統(tǒng)運行版本進(jìn)行約束的宏定義账蓉,主要為了兼容非Objective-C 2.0的遺留版本
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 消息發(fā)送 : iOS8以后的特殊寫法
((void(*)(id,SEL,id))objc_msgSend)(self,@selector(testDemo:),nil);
}
- (void)testDemo:(id)param {
NSLog(@"%s",__func__);
}
數(shù)據(jù)類型分析
SEL : 方法選擇器(指向objc_selector結(jié)構(gòu)體的指針)
typedef struct objc_selector *SEL;
1.可以通過Objc編譯器命令@selector()或者Runtime系統(tǒng)的sel_registerName()函數(shù)來獲取一個SEL類型的方法選擇器.
2.如果知道selector對應(yīng)的方法名是什么,可以通過NSString* NSStringFromSelector(SEL aSelector)方法將SEL轉(zhuǎn)化為OC字符串
id : 對象(指向objc_object結(jié)構(gòu)體的指針)
// objc_object結(jié)構(gòu)體
struct objc_object {
// id的成員 : isa
Class isa OBJC_ISA_AVAILABILITY;
};
// 指向objc_object結(jié)構(gòu)體指針
typedef struct objc_object *id;
1.包含一個Class isa成員.
2.根據(jù)isa指針就可以找到對象所屬的類.
Class : 對象所屬的類(指向objc_class結(jié)構(gòu)體的指針)<if見面自動加井號>
typedef struct objc_class *Class;
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;
Method : 方法(指向objc_method結(jié)構(gòu)體的指針)
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
objc_method存儲了方法名(method_name)逾一、方法類型(method_types)和方法實現(xiàn)(method_imp)等信息.
method_imp的數(shù)據(jù)類型是IMP铸本,它是一個函數(shù)指針.
IMP : 方法實現(xiàn)(指向方法實現(xiàn)的指針)
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
IMP本質(zhì)上就是一個函數(shù)指針,指向方法的實現(xiàn).
當(dāng)向某個對象發(fā)送一條信息時遵堵,可以由這個函數(shù)指針來指定方法的實現(xiàn)箱玷,它最終就會執(zhí)行那段代碼.
Ivar : 實例變量(指向objc_ivar結(jié)構(gòu)體的指針)
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
Cache : 緩存(指向結(jié)構(gòu)體objc_cache的指針)
typedef struct objc_cache *Cache OBJC2_UNAVAILABLE;
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
}
其實就是一個存儲Method的鏈表,主要是為了優(yōu)化方法調(diào)用的性能.
類關(guān)系圖
1.實例對象在運行時被表示成 objc_object 類型結(jié)構(gòu)體陌宿,結(jié)構(gòu)體內(nèi)部有個isa指針指向 objc_class 結(jié)構(gòu)體锡足。
2.objc_class 內(nèi)部保存了類的變量和方法列表以及其他一些信息,并且還有一個isa指針壳坪。這個isa指針會指向 metaClass(元類)舶得,元類里保存了這個類的類方法列表。
3.元類里也有一個isa指針爽蝴,這個isa指針沐批,指向的是根元類,根元類的isa指針指向自己
objc_class中信息查看
Class : 對象所屬的類(指向objc_class結(jié)構(gòu)體的指針)
typedef struct objc_class *Class;
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; // 位標(biāo)識
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;
代碼示例
獲取類名
/**
獲取類名
@param cls 要獲取類名的類
@return 類名
*/
+ (NSString *)getClassName:(Class)cls {
// 獲取類名(C語言類型)
const char *cName = class_getName(cls);
// OC類型類名
NSString *className = [NSString stringWithUTF8String:cName];
return className;
}
獲取成員變量列表
/**
獲取成員變量列表(帶下劃線的都獲取)
@param cls 要獲取成員變量列表的類
@return 成員變量數(shù)組(成員變量名字和類型組合的字典數(shù)組)
*/
+ (NSArray *)getIvarList:(Class)cls {
// 成員變量個數(shù)
unsigned int count;
// 獲取所有的成員變量
Ivar *ivarList = class_copyIvarList(cls, &count);
// 準(zhǔn)備數(shù)組容器
NSMutableArray *ivarArrM = [NSMutableArray arrayWithCapacity:count];
// 遍歷成員變量
for (NSInteger i = 0; i < count; i++) {
// 獲取成員變量名字
const char *ivarName = ivar_getName(ivarList[i]);
// 獲取成員變量類型
const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
// 成員變量名字和類型組合的字典容器
NSMutableDictionary *ivarDictM = [NSMutableDictionary dictionary];
ivarDictM[@"name"] = [NSString stringWithUTF8String:ivarName];
ivarDictM[@"type"] = [NSString stringWithUTF8String:ivarType];
// 添加到數(shù)組容器
[ivarArrM addObject:ivarDictM];
}
free(ivarList);
return ivarArrM.copy;
}
獲取屬性列表
/**
獲取屬性列表(有setter和getter方法的屬性)
@param cls 要獲取屬性列表的類
@return 屬性數(shù)組(屬性名字和屬性描述的字典數(shù)組)
*/
+ (NSArray *)getPropertyList:(Class)cls {
// 成員屬性個數(shù)
unsigned int count;
// 獲取所有成員變量
objc_property_t *propertyList = class_copyPropertyList(cls, &count);
// 成員屬性容器
NSMutableArray *propertyArrM = [NSMutableArray arrayWithCapacity:count];
// 遍歷成員屬性
for (NSInteger i = 0; i < count; i++) {
// 獲取屬性名字和屬性的屬性描述
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
NSString *attr = [NSString stringWithUTF8String:property_getAttributes(property)];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[@"name"] = name;
dict[@"attr"] = attr;
// 添加到數(shù)組容器
[propertyArrM addObject:[dict copy]];
}
free(propertyList);
return propertyArrM.copy;
}
類屬性中的屬性示例
@property (nonatomic,strong, setter=setPublicProperty:) NSArray *publicProperty01;
property_getAttributes輸出為:
attr = "T@\"NSArray\",&,N,SsetPublicProperty:,V_publicProperty01"; name = publicProperty01;
其中 attr 的解釋為:
- T 代表類型標(biāo)識
- @ 代表為對象類型
- NSArray 表示其實際類型
- & 代表 retain 強(qiáng)引用(copy 用 C, weak 用 W)
- N 代表 nonatomic (代表 natomic)
- SsetPublicProperty: 前面大寫S代表指定了 setter蝎亚,后面跟著代表具體方法
- V_publicProperty01 V代表其對應(yīng)的成員九孩,后面為成員的名字
獲取方法列表
/**
獲取類的實例方法 : 屬性的setter和getter方法,對象方法,不包括類方法
@param cls 要獲取類的實例方法的類
@return 方法數(shù)組
*/
+ (NSArray *)getMethodList:(Class)cls {
// 實例方法個數(shù)
unsigned int count;
// 獲取所有方法(不包括類方法)
Method *methodList = class_copyMethodList(cls, &count);
// 方法容器
NSMutableArray *methodArrM = [NSMutableArray arrayWithCapacity:count];
// 遍歷所有方法
for (NSInteger i = 0; i < count; i++) {
// 獲取數(shù)據(jù)
NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[@"name"] = name;
dict[@"type"] = type;
// 添加到數(shù)組
[methodArrM addObject:[dict copy]];
}
// 通過元類獲取類方法
Class metaCls = objc_getMetaClass(class_getName(cls));
methods = class_copyMethodList(metaCls, &count);
for (NSInteger i = 0; i < count; i++) {
Method method = methods[i];
// 獲取數(shù)據(jù)
NSString *name = [NSString stringWithString:NSStringFromSelector(method_getName(method))];
NSString *type = [NSString stringWithUTF8String:method_getTypeEncoding(method)];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
dict[@"name"] = name;
dict[@"type"] = type;
// 添加到數(shù)組
[arrayM addObject:[dict copy]];
}
free(methodList);
return methodArrM.copy;
}
方法類型編碼示例
/// 公有方法2
- (void)publicMethodd02:(NSString *)str append:(int)age;
method_getTypeEncoding 的輸出為:
name = "publicMethodd02:append:"; type = "v28@0:8@16i24";
其中type解釋為:
- v 代表返回值為void
- 28 代表整個方法參數(shù)占位的總長度
- @0 @代表對象,objc_msgSend 函數(shù)傳入的第1個參數(shù)(self), 后面的0代表位置0開始
- :8 :代表SEL发框,objc_msgSend 函數(shù)傳入的第2個參數(shù)(self)躺彬,后面的8代表位置8開始
- @16 @代表第1個參數(shù)的類型為對象類型,后面的16代表位置8開始
- i24 i代表第2個參數(shù)的類型為int類型缤底,后面的24代表位置24開始
獲取協(xié)議列表
/**
獲取類的協(xié)議列表
@param cls 獲取協(xié)議列表的類
@return 協(xié)議數(shù)組
*/
+ (NSArray *)getProtocolList:(Class)cls {
// 協(xié)議個數(shù)
unsigned int count;
// 獲取協(xié)議列表
Protocol * __unsafe_unretained *protocolList = class_copyProtocolList(cls, &count);
// 協(xié)議容器
NSMutableArray *protocolArrM = [NSMutableArray arrayWithCapacity:count];
// 遍歷協(xié)議列表
for (NSInteger i = 0; i < count; i++) {
// 獲取協(xié)議名字
const char *protocolName = protocol_getName(protocolList[i]);
// 添加到協(xié)議容器
[protocolArrM addObject:[NSString stringWithUTF8String:protocolName]];
}
free(protocolList);
return protocolArrM.copy;
}