runtime 理解及實際應用

首先 , 什麼是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;

目前就簡單總結一點, 純屬個人理解, 歡迎指正溝通!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章