??Runtime 一個(gè)耳熟能詳?shù)脑~語(yǔ),但是你知道它具體是啥?能干些什么嘛?那么就由小編帶你去探索下Runtime這扇神秘的大門(mén)吧。
本文內(nèi)容:
什么是Runtime?
Runtime能做什么?(動(dòng)態(tài)添加方法、方法交換、獲取類(lèi)的屬性和方法列表)
一、什么是Runtime?
Runtime是Objective-C的一個(gè)重要特性,它是一組在運(yùn)行時(shí)執(zhí)行的C函數(shù),提供了一些在編譯時(shí)無(wú)法確定的特性,如動(dòng)態(tài)類(lèi)型識(shí)別、動(dòng)態(tài)方法調(diào)用等。
在iOS和macOS開(kāi)發(fā)中,開(kāi)發(fā)者通常使用Runtime來(lái)進(jìn)行一些高級(jí)的操作,如方法交換、動(dòng)態(tài)添加方法等。
二、Runtime能做些什么?
1、動(dòng)態(tài)添加方法
??Runtime允許在運(yùn)行時(shí)動(dòng)態(tài)添加方法,這對(duì)于一些動(dòng)態(tài)創(chuàng)建類(lèi)或者在運(yùn)行時(shí)實(shí)現(xiàn)某些特定功能的情況非常有用。
#import "MyClass.h"
#import <objc/runtime.h>
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if(sel == @selector(dynamicMethod)) {
class_addMethod([self class], sel, (IMP)dynamicMethodImplementation, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void dynamicMethodImplementation(id self, SEL _cmd) {
NSLog(@"Dynamic Method Implementation");
}
@end
參數(shù)解析:
1、`v`:方法的返回類(lèi)型是`void`,表示該方法沒(méi)有返回值。
2、`@`:第一個(gè)參數(shù)的類(lèi)型是Objective-C對(duì)象。
3、`:`:表示方法有一個(gè)參數(shù)。
所以,`v@:`表示一個(gè)沒(méi)有返回值的方法,該方法有一個(gè)參數(shù),而這個(gè)參數(shù)的類(lèi)型是Objective-C對(duì)象,在實(shí)際使用中,`v@:`的方法通常是用于處理事件,比如按鈕點(diǎn)擊時(shí)觸發(fā)的方法。
哪些情況下合適?
1、`處理未知的方法調(diào)用`:當(dāng)你無(wú)法提前知道某個(gè)方法是否存在,但在運(yùn)行時(shí)需要根據(jù)某些條件是否添加該方法的實(shí)現(xiàn)時(shí);
2、`動(dòng)態(tài)生成方法`:當(dāng)某個(gè)類(lèi)有大量方法,但在特定情況下只需動(dòng)態(tài)生成或添加其中的一部分方法時(shí);
3、`根據(jù)屬性名動(dòng)態(tài)生成默認(rèn)值`:在一些需要頻繁添加或修改屬性的場(chǎng)景下,使用動(dòng)態(tài)方法解析 生成默認(rèn)值可以使代碼更加具有可維護(hù)性,避免了頻繁手動(dòng)管理默認(rèn)值的繁瑣工作;
4、`實(shí)現(xiàn)消息轉(zhuǎn)發(fā)機(jī)制`:動(dòng)態(tài)方法解析是Objective-C消息轉(zhuǎn)發(fā)機(jī)制的一部分,當(dāng)Runtime無(wú)法找到匹配的方法時(shí),
就會(huì)調(diào)用`+ (BOOL)resolveInstanceMethod`方法。通過(guò)動(dòng)態(tài)方法解析,你可以在這個(gè)階段決定是否添加方法的實(shí)現(xiàn)或者將消息轉(zhuǎn)發(fā)給其他對(duì)象
...
PS:雖然動(dòng)態(tài)方法解析提供了一些靈活性,但在項(xiàng)目中過(guò)度使用,可能會(huì)導(dǎo)致代碼難以理解和維護(hù)。
建議在確實(shí)需要在運(yùn)行時(shí)動(dòng)態(tài)處理方法的情況下使用,而在其他情況下,最好在編譯時(shí)進(jìn)行方法聲明。
2、方法交換
??交換兩個(gè)方法的實(shí)現(xiàn),通常用于在不改變?cè)写a的情況下修改方法的行為,常用于在某些情況下打印方法調(diào)用日志或者統(tǒng)計(jì)方法消耗時(shí)等。
#import "MyClass.h"
#import <objc/runtime.h>
@implementation MyClass
- (void)originalMethod {
NSLog(@"Original Method");
}
- (void)swizzledMethod {
NSLog(@"Swizzled Method");
}
+ (void)load {
// dispatch_once 確保這個(gè)方法只執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(originalMethod);
SEL swizzledSelector = @selector(swizzledMethod);
// class_getInstanceMethod 獲取originalMethod和swizzledMethod的方法實(shí)現(xiàn)
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
/**
* class_addMethod 嘗試將`originalMethod`的實(shí)現(xiàn)替換為`swizzledMethod`,這樣如果`originalMethod`不存在,就添加`swizzledMethod`的實(shí)現(xiàn)
* 如果返回成功,說(shuō)明`originalMethod`不存在,直接替換成功,否則,使用`method_exchangeImplementations`交換兩個(gè)方法的實(shí)現(xiàn)
**/
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
//將`originalMethod`的實(shí)現(xiàn)替換為`swizzledMethod`
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
// 交換兩個(gè)方法的實(shí)現(xiàn)
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
@end
項(xiàng)目中什么情況下使用?
1、`日志記錄和調(diào)試`:通過(guò)方法交換,你可以在方法調(diào)用前后插入日志記錄的邏輯,方便調(diào)試和監(jiān)控程序行為;
2、`性能統(tǒng)計(jì)`:可以用在方法調(diào)用前后記錄時(shí)間戳,從而進(jìn)行性能統(tǒng)計(jì)和監(jiān)測(cè),了解方法的耗時(shí)情況;
3、`無(wú)侵入地修改現(xiàn)有功能`:如果你在不想修改原有代碼的情況下修改某個(gè)方法的行為,可以使用方法交換。這對(duì)于在第三方庫(kù)中修改某些行為或修復(fù)Bug非常有用。
4、`AOP(面向切面編程)`:方法交換是AOP的一種實(shí)現(xiàn)方式,通過(guò)在方法調(diào)用的前后插入代碼,可以實(shí)現(xiàn)一些橫切關(guān)注點(diǎn)的邏輯,比如日志、權(quán)限檢查等。
5、`熱修復(fù)`:在熱修復(fù)框架中,方法交換常被用于替換應(yīng)用中的某些方法,以修復(fù)應(yīng)用程序中的Bug或者緊急情況。
PS:使用方法交換也是會(huì)導(dǎo)致代碼變得難以理解和維護(hù),所以要慎重考慮。在項(xiàng)目中使用方法交換時(shí),建議進(jìn)行充分的測(cè)試和文檔記錄,以確保代碼的可維護(hù)性和穩(wěn)定性。
3、獲取類(lèi)的屬性和方法列表:
??可以獲取類(lèi)的屬性和方法列表,這在一些運(yùn)行時(shí)動(dòng)態(tài)處理屬性和方法的場(chǎng)景中非常有用。
unsigned int count;
//class_copyPropertyList 用于獲取指定類(lèi)的屬性列表,返回一個(gè)指向?qū)傩粤斜淼闹羔? objc_property_t *properties = class_copyPropertyList([MyClass class], &count);
for (int i = 0; i < count ; i++) {
//property_getName用于獲取屬性的名稱(chēng)
const char *propertyName = property_getName(properties[i]);
NSLog(@"Property:%s",propertyName);
}
// free釋放內(nèi)存,因?yàn)閌class_copyPropertyList`返回的指針需要手動(dòng)釋放
free(properties);
// class_copyMethodList 用于獲取指定類(lèi)的方法列表,返回一個(gè)指向方法列表的指針
Method *methods = class_copyMethodList([MyClass class], &count);
for (int i = 0; i < count; i++) {
// method_getName 用于獲取方法的選擇器(SEL)
SEL methodName = method_getName(methods[i]);
NSLog(@"Method:%@", NSStringFromSelector(methodName));
}
// free釋放內(nèi)存,因?yàn)閌class_copyMethodList`返回的指針需要手動(dòng)釋放
free(methods);
哪些情況下需要用到?
1、在運(yùn)行時(shí)動(dòng)態(tài)地設(shè)置對(duì)象的屬性
// 獲取類(lèi)的屬性列表
unsigned int count;
objc_property_t *properties = class_copyPropertyList([MyClass class], &count);
// 設(shè)置對(duì)象屬性的值
id myObject = [[MyClass alloc] init];
for (int i = 0; i < count; i++) {
const char *propertyName = property_getName(properties[i]);
NSString *propertyNameString = [NSString stringWithUTF8String:propertyName];
[myObject setValue:@"New Value" forKey:propertyNameString];
}
free(properties);
2、將對(duì)象轉(zhuǎn)換為一種可傳輸或可存儲(chǔ)的格式
// 獲取類(lèi)的屬性列表
unsigned int count;
objc_property_t *properties = class_copyPropertyList([MyClass class], &count);
// 將對(duì)象屬性轉(zhuǎn)換為字典進(jìn)行序列化
NSMutableDictionary *serializedObject = [NSMutableDictionary dictionary];
id myObject = [[MyClass alloc] init];
for (int i = 0; i < count; i++) {
const char *propertyName = property_getName(properties[i]);
NSString *propertyNameString = [NSString stringWithUTF8String:propertyName];
id propertyValue = [myObject valueForKey:propertyNameString];
[serializedObject setObject:propertyValue forKey:propertyNameString];
}
free(properties);
3、自動(dòng)生成代碼,例如根據(jù)數(shù)據(jù)模型生成數(shù)據(jù)庫(kù)操作代碼
// 獲取類(lèi)的方法列表
unsigned int count;
Method *methods = class_copyMethodList([MyClass class], &count);
// 自動(dòng)生成代碼
for (int i = 0; i < count; i++) {
SEL methodName = method_getName(methods[i]);
NSString *methodSignature = NSStringFromSelector(methodName);
NSLog(@"Generated code for method: %@", methodSignature);
}
free(methods);
未完待續(xù)...