IOS 運行時(runtime)機制

1. 概述

OC 是一個全動態語言,OC 的一切都是基於 Runtime 實現的

只有在程序運行時,纔會去確定對象的類型,並調用類與對象相應的方法`

2. 運行時機制

運行時機制是用 C++ 開發的,是一套蘋果開源的框架
OC 是基於運行時開發的語言

3. 應用場景

運行時動態獲取類的屬性

主要應用:

  • 字典轉模型框架 MJExtension,JSONModel
  • 利用 關聯對象 爲分類添加屬性
  • 利用 交換方法 攔截系統或其他框架的方法
  • 誤區:並不是使用的技術越底層,框架的效率就會越高

導入頭文件

#import <objc/runtime.h>

4. 示例

爲NSObject添加一個分類

//
//  NSObject+Extension.m
//

#import "NSObject+Extension.h"
#import <objc/runtime.h>

@implementation NSObject (Extension)

const char *propertiesKey = "propertiesKey";
const char *methodsKey = "methodsKey";
const char *protocolKey = "protocolKey";

/// 字典轉模型方法
+ (instancetype)objectWithDict:(NSDictionary *)dict
{
    id obj = [[self alloc] init];

    // 獲取屬性列表
    NSArray *properties = [self propertyList];

    // 遍歷屬性數組
    for (NSString *key in properties) 
    {
        // 判斷字典中是否包含這個key
        if (dict[key] != nil) 
        {
            // 使用 KVC 設置屬性值
            [obj setValue:dict[key] forKeyPath:key];
        }
    }

    return obj;
}

/**
 如果能夠自動生成這個數組,就好了!
 如果要想動態的獲取類的屬性,需要使用到“運行時機制”

 class_copyIvarList 成員變量,提示有很多第三方框架會使用 Ivar,能夠獲得更多的信息
 但是:在 swift 中,由於語法結構的變化,使用 Ivar 非常不穩定,經常會崩潰!
 class_copyPropertyList 屬性
 class_copyMethodList 方法
 class_copyProtocolList 協議
 */
/// 返回類的屬性列表
+ (NSArray *)propertyList 
{

    // 0. 判斷是否存在關聯對象, 如果存在, 直接返回
    /** 參數
     1> 關聯到得對象
     2> 關聯的屬性 key
     */
    NSArray *plist = objc_getAssociatedObject(self, propertiesKey);
    if (plist) 
    {
        return plist;
    }

    // 1. 獲取 '類的屬性'
    /**
     1> 類
     2> 屬性的計數指針
     */
    unsigned int count = 0;
    // 返回值是所有屬性的數組 obj_property_
    objc_property_t *list = class_copyPropertyList([self class], &count);

    // 創建 存儲屬性數組
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];

    for (int i = 0; i < count; ++i) 
    {

        // 獲取屬性
        objc_property_t pty = list[i];

        // 獲取屬性名
        const char *cname = property_getName(pty);
        [arrayM addObject:[NSString stringWithUTF8String:cname]];
    }

    // 釋放屬性數組
    free(list);

    // 設置關聯對象
    /**
     1> 關聯的對象
     2> 關聯對象的 key
     3> 屬性數值
     4> 屬性的持有方式 reatin, copy, assign
     */
    objc_setAssociatedObject(self, propertiesKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);

    return arrayM.copy;
}

/// 返回類的方法列表
+ (NSArray *)methodList 
{

    // 0. 判斷是否存在依賴關係
    NSArray *mlist = objc_getAssociatedObject(self, methodsKey);
    if (mlist) 
    {
        return mlist;
    }

    unsigned int count = 0;
    // 1. 獲取方法列表
    Method *list = class_copyMethodList([self class], &count);

    // 存儲方法數組
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];

    for (int i = 0; i < count; ++i) 
    {
        // 獲取方法
        Method method = list[i];
        // 獲取方法名
        SEL mname = method_getName(method);

        [arrayM addObject: NSStringFromSelector(mname)];
    }

    // 釋放數組
    free(list);

    // 設置依賴關係
    objc_setAssociatedObject(self, methodsKey, arrayM, OBJC_ASSOCIATION_COPY_NONATOMIC);

    return arrayM.copy;
}

/// 返回類的實現協議列表
+ (NSArray *)protocolList
{

    // 0. 判斷是否存在依賴關係
    NSArray *pList = objc_getAssociatedObject(self, protocolKey);
    if (pList) 
    {
        return pList;
    }

    unsigned int count = 0;
    // 2. 獲取協議列表
    Protocol * __unsafe_unretained *list = class_copyProtocolList([self class], &count);

    // 創建協議數組
    NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:count];

    for (int i = 0; i < count; ++i) 
    {

        // 獲取協議
        Protocol * __unsafe_unretained prot = list[i];

        const char *pname = protocol_getName(prot);

        [arrayM addObject:[NSString stringWithUTF8String:pname]];
    }

    return arrayM;
}
@end
發佈了42 篇原創文章 · 獲贊 9 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章