首先 , 什麼是runtime?
1>OC 是一個全動態語言,OC 的一切都是基於 Runtime 實現的
平時編寫的OC代碼, 在程序運行過程中, 其實最終都是轉成了runtime的C語言代碼, runtime算是OC的幕後工作者, 例如:
OC :
[[Person alloc] init]
runtime :
objc_msgSend(objc_msgSend("Person" , "alloc"), "init")
2>runtime是一套比較底層的純C語言API, 屬於1個C語言庫, 包含了很多底層的C語言API
3>runtimeAPI的實現是用 C++ 開發的(源碼中的實現文件都是mm),是一套蘋果開源的框架
簡單的概括一下就是 :OC語言儘可能地把數據類型的確定從編譯時和鏈接時移到了運行時, OC具備運行時特性就意味着 Objective-C 語言不僅需要一個編譯器,同時也需要一個運行時系統來執行編譯好的代碼。
下面簡單說一下runtime的幾種使用方法:
- 方法替換 (如:避免數組加入nil, 導致crash)
/*
如果沒有引入"NSMutableArray+Extension.h" ,運行crash報錯 “object cannot be nil”
反之 引入"NSMutableArray+Extension.h", 運行正常, 原因就在於 在NSMutableArray+Extension 裏使用了runtime 修改了addObject方法,判斷如果是你nil 將不會在向數組裏添加空對象 ,因此不會再報錯
*/
[self.mArray addObject:@"第一個"];
[self.mArray addObject:@"第一個"];
[self.mArray addObject:nil];
NSLog(@"%@", self.mArray);
runtime實現代碼
#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>
@implementation NSMutableArray (Extension)
+(void)load
{
Method oldMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(addObject:));
Method newMethod = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(newAddObject:));
method_exchangeImplementations(oldMethod, newMethod);
}
- (void)newAddObject:(id)object{
if (object != nil) {
[self newAddObject:object];
}
}
@end
這裏做的就是替換addObject方法, 進而達到免疫nil的目地
- 給一個類添加屬性
在這裏我給NSObject添加了一個name屬性, 代碼如下:
#import "NSObject+Property.h"
#import <objc/runtime.h>
static const NSString *key = @"name";
@implementation NSObject (Property)
- (NSString *)name
{
// 根據關聯的key,獲取關聯的值。
return objc_getAssociatedObject(self, (__bridge const void *)(key));
}
- (void)setName:(NSString *)name
{
// 第一個參數:給哪個對象添加關聯
// 第二個參數:關聯的key,通過這個key獲取
// 第三個參數:關聯的value
// 第四個參數:關聯的策略
objc_setAssociatedObject(self, (__bridge const void *)(key), name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
驗證代碼:
//用於給一個類添加屬性
- (void)runTimeTestForAddProperty
{
NSObject *obj = [[NSObject alloc] init];
obj.name = @"我是新加的";
NSLog(@"%@", obj.name);
}
運行之後打印出:” 我是新加的“, 證明屬性添加成功
- 利用runtime 進行json轉model
- (id)initWithDic:(NSDictionary *)dic {
if (self = [super init]) {
unsigned int outCount = 0;
Class currentClass = [self class];
while (currentClass) {
//獲取當前類所有的屬性
objc_property_t *properties = class_copyPropertyList(currentClass, &outCount);
for (int i = 0; i < outCount; i++) {
//獲取該類的一個屬性指針
objc_property_t property = properties[i];;
//property_getName(property) 獲取屬性的名稱
//initWithCString, c的字符串轉OC的字符串
NSString *propertyNameString =[[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
//因爲id是一個OC的關鍵字, 你不能聲明一個屬性名稱爲id, 所以遇到屬性名稱是id就攜程id_i
//如果屬性名稱是id_i, 則把id賦值給當前屬性
if ([propertyNameString isEqualToString:@"id_i"]) {
propertyNameString = @"id";
}
id value;
//判斷傳過來的參數是不是字典類型
if ([dic isKindOfClass:[NSDictionary class]]) {
//根據key獲取json數據對應的value
//如果對應的可以不存在, 使用字典去獲取一個value值, 則返回值爲nil
value = [dic objectForKey:propertyNameString];
}
//如果該值不存在
if (value == [NSNull null]|| value == nil) {
//則返回繼續尋找
continue;
}
//如果值存在
if (value) {
//但度處理名稱爲id的
if ([propertyNameString isEqualToString:@"id"]) {
[self setValue:[dic objectForKey:@"id"] forKey:@"id_i"];
}else{
//根據屬性, 使用kvc賦值
[self setValue:value forKey:propertyNameString];
}
}
}
//最後別忘記釋放
free(properties);
Class superclass = [currentClass superclass];
currentClass = superclass;
}
}
return self;
}
這樣做的好處就是 防止因爲後臺返回的字段沒有或者key值錯誤,進而導致model轉換出現問題
用於多參數傳遞
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"我來測試一下" message:@"" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}];
UIAlertAction *action1 = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSString *number1 = objc_getAssociatedObject(alert, "number1");
NSString *number2 = objc_getAssociatedObject(alert, "number2");
NSLog(@"%@ %@", number1, number2);
}];
// 傳遞多參數
objc_setAssociatedObject(alert, "number1", @"1", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_setAssociatedObject(alert, "number2", @"2", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[alert addAction:action];
[alert addAction:action1];
[self presentViewController:alert animated:YES completion:nil];
點擊確定按鈕 打印1 2 , 說明傳了兩個參數 1 2;
目前就簡單總結一點, 純屬個人理解, 歡迎指正溝通!