Runtime理解
Runtime簡稱執行時。OC就是執行時機制,也就是在執行時候的一些機制,其中最主要的是訊息機制。
對於C語言,函式的呼叫在編譯的時候會決定呼叫哪個函式。
對於OC的函式,屬於動態呼叫過程,在編譯的時候並不能決定真正呼叫哪個函式,只有在真正執行的時候才會根據函式的名稱找到對應的函式來呼叫。
在編譯階段,OC可以呼叫任何函式,即使這個函式並未實現,只要宣告過就不會報錯。
在編譯階段,C語言呼叫未實現的函式就會報錯。
runtime作用
1.傳送訊息
方法呼叫的本質,就是讓物件傳送訊息。
objc_msgSend,只有物件才能傳送訊息,因此以objc開頭.
使用訊息機制前提,必須匯入#import <objc/message.h>
訊息機制原理:物件根據方法編號SEL去對映表查詢對應的方法實現
2.交換方法
開發使用場景:系統自帶的方法功能不夠,給系統自帶的方法擴充套件一些功能,並且保持原有的功能。
方式一:繼承系統的類,重寫方法.
方式二:使用runtime,交換方法.
3.動態新增方法
開發使用場景:如果一個類方法非常多,載入類到記憶體的時候也比較耗費資源,需要給每個方法生成對映表,可以使用動態給某個類,新增方法解決。
經典面試題:有沒有使用performSelector,其實主要想問你有沒有動態新增過方法。
4.給分類新增屬性
- 原理:給一個類宣告屬性,其實本質就是給這個類新增關聯,並不是直接把這個值的記憶體空間新增到類存空間。
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib. // 給系統NSObject類動態新增屬性name
NSObject *objc = [[NSObject alloc] init];
objc.name = @"小碼哥";
NSLog(@"%@",objc.name);
}
@end
// 定義關聯的keystatic const char *key = "name";
@implementation NSObject (Property)
- (NSString *)name{
// 根據關聯的key,獲取關聯的值。
return objc_getAssociatedObject(self, key);
}
- (void)setName:(NSString *)name{
// 第一個引數:給哪個物件新增關聯
// 第二個引數:關聯的key,通過這個key獲取
// 第三個引數:關聯的value
// 第四個引數:關聯的策略
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);}
@end
5.字典轉模型
設計模型:字典轉模型的第一步
模型屬性,通常需要跟字典中的key一一對應
問題:一個一個的生成模型屬性,很慢?
需求:能不能自動根據一個字典,生成對應的屬性。
解決:提供一個分類,專門根據字典生成對應的屬性字串。
@implementation NSObject (Log)// 自動列印屬性字串+ (void)resolveDict:(NSDictionary *)dict{ // 拼接屬性字串程式碼 NSMutableString *strM = [NSMutableString string]; // 1.遍歷字典,把字典中的所有key取出來,生成對應的屬性程式碼 [dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { // 型別經常變,抽出來 NSString *type; if ([obj isKindOfClass:NSClassFromString(@"__NSCFString")]) { type = @"NSString"; }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFArray")]){ type = @"NSArray"; }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFNumber")]){ type = @"int"; }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFDictionary")]){ type = @"NSDictionary"; } // 屬性字串 NSString *str; if ([type containsString:@"NS"]) { str = [NSString stringWithFormat:@"@property (nonatomic, strong) %@ *%@;",type,key]; }else{ str = [NSString stringWithFormat:@"@property (nonatomic, assign) %@ %@;",type,key]; } // 每生成屬性字串,就自動換行。 [strM appendFormat:@"\n%@\n",str]; }]; // 把拼接好的字串列印出來,就好了。 NSLog(@"%@",strM);}@end
字典轉模型的方式一:KVC
@implementation Status+ (instancetype)statusWithDict:(NSDictionary *)dict{ Status *status = [[self alloc] init]; [status setValuesForKeysWithDictionary:dict]; return status;}@end
KVC字典轉模型弊端:必須保證,模型中的屬性和字典中的key一一對應。
如果不一致,就會呼叫[<Status 0x7fa74b545d60> setValue:forUndefinedKey:]報key找不到的錯。
分析:模型中的屬性和字典的key不一一對應,系統就會呼叫setValue:forUndefinedKey:報錯。
解決:重寫物件的setValue:forUndefinedKey:,把系統的方法覆蓋,就能繼續使用KVC,字典轉模型了。
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
字典轉模型的方式二:Runtime
思路:利用執行時,遍歷模型中所有屬性,根據模型的屬性名,去字典中查詢key,取出對應的值,給模型的屬性賦值。
步驟:提供一個NSObject分類,專門字典轉模型,以後所有模型都可以通過這個分類轉。
@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. // 解析Plist檔案 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil]; NSDictionary *statusDict = [NSDictionary dictionaryWithContentsOfFile:filePath]; // 獲取字典陣列 NSArray *dictArr = statusDict[@"statuses"]; // 自動生成模型的屬性字串// [NSObject resolveDict:dictArr[0][@"user"]]; _statuses = [NSMutableArray array]; // 遍歷字典陣列 for (NSDictionary *dict in dictArr) { Status *status = [Status modelWithDict:dict]; [_statuses addObject:status]; } // 測試資料 NSLog(@"%@ %@",_statuses,[_statuses[0] user]);}@end@implementation NSObject (Model)+ (instancetype)modelWithDict:(NSDictionary *)dict{ // 思路:遍歷模型中所有屬性-》使用執行時 // 0.建立對應的物件 id objc = [[self alloc] init]; // 1.利用runtime給物件中的成員屬性賦值 // class_copyIvarList:獲取類中的所有成員屬性 // Ivar:成員屬性的意思 // 第一個引數:表示獲取哪個類中的成員屬性 // 第二個引數:表示這個類有多少成員屬性,傳入一個Int變數地址,會自動給這個變數賦值 // 返回值Ivar *:指的是一個ivar陣列,會把所有成員屬性放在一個陣列中,通過返回的陣列就能全部獲取到。 /* 類似下面這種寫法 Ivar ivar; Ivar ivar1; Ivar ivar2; // 定義一個ivar的陣列a Ivar a[] = {ivar,ivar1,ivar2}; // 用一個Ivar *指標指向陣列第一個元素 Ivar *ivarList = a; // 根據指標訪問陣列第一個元素 ivarList[0]; */ unsigned int count; // 獲取類中的所有成員屬性 Ivar *ivarList = class_copyIvarList(self, &count); for (int i = 0; i < count; i++) { // 根據角標,從陣列取出對應的成員屬性 Ivar ivar = ivarList[i]; // 獲取成員屬性名 NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)]; // 處理成員屬性名->字典中的key // 從第一個角標開始擷取 NSString *key = [name substringFromIndex:1]; // 根據成員屬性名去字典中查詢對應的value id value = dict[key]; // 二級轉換:如果字典中還有字典,也需要把對應的字典轉換成模型 // 判斷下value是否是字典 if ([value isKindOfClass:[NSDictionary class]]) { // 字典轉模型 // 獲取模型的類物件,呼叫modelWithDict // 模型的類名已知,就是成員屬性的型別 // 獲取成員屬性型別 NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; // 生成的是這種@"@\"User\"" 型別 -》 @"User" 在OC字串中 \" -> ",\是轉義的意思,不佔用字元 // 裁剪型別字串 NSRange range = [type rangeOfString:@"\""]; type = [type substringFromIndex:range.location + range.length]; range = [type rangeOfString:@"\""]; // 裁剪到哪個角標,不包括當前角標 type = [type substringToIndex:range.location]; // 根據字串類名生成類物件 Class modelClass = NSClassFromString(type); if (modelClass) { // 有對應的模型才需要轉 // 把字典轉模型 value = [modelClass modelWithDict:value]; } } // 三級轉換:NSArray中也是字典,把陣列中的字典轉換成模型. // 判斷值是否是陣列 if ([value isKindOfClass:[NSArray class]]) { // 判斷對應類有沒有實現字典陣列轉模型陣列的協議 if ([self respondsToSelector:@selector(arrayContainModelClass)]) { // 轉換成id型別,就能呼叫任何物件的方法 id idSelf = self; // 獲取陣列中字典對應的模型 NSString *type = [idSelf arrayContainModelClass][key]; // 生成模型 Class classModel = NSClassFromString(type); NSMutableArray *arrM = [NSMutableArray array]; // 遍歷字典陣列,生成模型陣列 for (NSDictionary *dict in value) { // 字典轉模型 id model = [classModel modelWithDict:dict]; [arrM addObject:model]; } // 把模型陣列賦值給value value = arrM; } } if (value) { // 有值,才需要給模型的屬性賦值 // 利用KVC給模型中的屬性賦值 [objc setValue:value forKey:key]; } } return objc;}@end
相關文章
- 深入理解-dl_runtime_resolve
- Go GPM的理解 與 runtime包Go
- 深入理解Objective-C-Runtime-isaObject
- iOS開發Runtime的理解與應用iOS
- Runtime(一)
- Runtime(二)
- Runtime類
- RunTime和RunLoopOOP
- Runtime小結
- Object Runtime -- WeakObject
- Runtime 、 SEL and KVO
- 深度剖析 Runtime
- ArcGIS Runtime For AndroidAndroid
- iOS開發之runtime(一):runtime除錯環境搭建iOS除錯
- iOS RunTime 總結iOS
- Runtime原始碼 autoreleasepool原始碼
- Goroutines: the dark side of the runtimeGoIDE
- java的runtime APIJavaAPI
- Runtime備忘-isa
- Go: sysmon, Runtime MonitoringGo
- ObjC Runtime簡析OBJ
- iOS Runtime詳解iOS
- Objective-C RuntimeObject
- ufs runtime suspend/resume
- runtime載入過程
- iOS 防止Crash之runtimeiOS
- Runtime知識點整理
- Runtime 原始碼閱讀原始碼
- JAVA常用類--Runtime類Java
- 18-containers-Runtime ClassAI
- go runtime debug 小技巧Go
- The runtime data area model of JVMJVM
- RunTime實現原理剖析
- Runtime面試之weak面試
- 探秘Runtime - 深入剖析CategoryGo
- elasticsearch中使用runtime fieldsElasticsearch
- Runtime-(六)Method-Swizzling
- Runtime原始碼 protocol(協議)原始碼Protocol協議