Runtime實際應用場景詳解

目錄

1.給分類增加屬性
2.方法添加和替換和KVO實現(xiàn)
3.weak 釋放 nil 的過程
4.消息轉發(fā)(熱更新)解決 Bug(JSPatch)
5.實現(xiàn) NSCoding 的自動歸檔和自動解檔
6.實現(xiàn)字典和模型的自動轉換(MJExtension)
7.[self class] 和 [super class]
8.Runtime補充說明

相關鏈接:

https://juejin.cn/post/6844903586216804359
http://www.reibang.com/p/c85e478d984c
https://juejin.cn/post/6844904079957688328
https://juejin.cn/post/6921348387501834254

1.關聯(lián)對象(Objective-C Associated Objects)給分類增加屬性

我們都是知道分類是不能自定義屬性和變量的绎晃,下面通過關聯(lián)對象實現(xiàn)給分類添加屬性。關聯(lián)對象 Runtime 提供了幾個接口如下代碼。

其中參數(shù)解釋為:
id object:被關聯(lián)的對象
const void *key:關聯(lián)的key奸晴,要求唯一
id value:關聯(lián)的對象
objc_AssociationPolicy policy:內存管理的策略

//關聯(lián)對象
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//獲取關聯(lián)的對象
id objc_getAssociatedObject(id object, const void *key)
//移除關聯(lián)的對象
void objc_removeAssociatedObjects(id object)
  • 內存管理的策略

下面我們看看內存策略對應的屬性修飾击敌。

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,           /**< Specifies a weak reference to the associated object. */
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,   /**< Specifies that the associated object is copied. 
                                            *   The association is not made atomically. */
    OBJC_ASSOCIATION_RETAIN = 01401,       /**< Specifies a strong reference to the associated object.
                                            *   The association is made atomically. */
    OBJC_ASSOCIATION_COPY = 01403          /**< Specifies that the associated object is copied.
                                            *   The association is made atomically. */
};
內存策略對應的屬性修飾
  • 舉例-下面實現(xiàn)一個 UIView 的 Category 添加自定義屬性 defaultColor:
#import "ViewController.h"
#import "objc/runtime.h"

@interface UIView (DefaultColor)

@property (nonatomic, strong) UIColor *defaultColor;

@end

@implementation UIView (DefaultColor)

@dynamic defaultColor;

static char kDefaultColorKey;

- (void)setDefaultColor:(UIColor *)defaultColor {
    objc_setAssociatedObject(self, &kDefaultColorKey, defaultColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (id)defaultColor {
    return objc_getAssociatedObject(self, &kDefaultColorKey);
}

@end

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    UIView *test = [UIView new];
    test.defaultColor = [UIColor blackColor];
    NSLog(@"%@", test.defaultColor);
}

@end

打印結果: 2018-04-01 15:41:44.977732+0800 ocram[2053:63739] UIExtendedGrayColorSpace 0 1

打印結果來看,我們成功在分類上添加了一個屬性,實現(xiàn)了它的 setter 和 getter 方法淌实。通過關聯(lián)對象實現(xiàn)的屬性的內存管理也是有 ARC 管理的,所以我們只需要給定適當?shù)膬却娌呗跃托辛瞬螅恍枰傩膶ο蟮尼尫拧?/p>

2.方法魔法(Method Swizzling)方法添加和替換和KVO實現(xiàn)

  • 2.1 方法添加

實際上添加方法在講消息轉發(fā)的時候拆祈,動態(tài)方法解析的時候就提到了。

  • cls 被添加方法的類
  • name 添加的方法的名稱的SEL
  • imp 方法的實現(xiàn)倘感。該函數(shù)必須至少要有兩個參數(shù)放坏,self,_cmd
  • 類型編碼
//class_addMethod(Class  _Nullable __unsafe_unretained cls, SEL  _Nonnull name, IMP  _Nonnull imp, const char * _Nullable types)
class_addMethod([self class], sel, (IMP)fooMethod, "v@:");
  • 2.2 方法替換

例子1:實現(xiàn)替換 ViewController 的 viewDidLoad 方法

其中:

1.swizzling 應該只在 +load 中完成。 在 Objective-C 的運行時中老玛,每個類有兩個方法都會自動調用淤年。+load 是在一個類被初始裝載時調用,+initialize 是在應用第一次調用該類的類方法或實例方法前調用的逻炊。兩個方法都是可選的互亮,并且只有在方法被實現(xiàn)的情況下才會被調用。

2.swizzling 應該只在 dispatch_once 中完成余素,由于 swizzling 改變了全局的狀態(tài)豹休,所以我們需要確保每個預防措施在運行時都是可用的。原子操作就是這樣一個用于確保代碼只會被執(zhí)行一次的預防措施桨吊,就算是在不同的線程中也能確保代碼只執(zhí)行一次威根。Grand Central Dispatch 的 dispatch_once 滿足了所需要的需求,并且應該被當做使用 swizzling 的初始化單例方法的標準视乐。

@implementation ViewController

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        SEL originalSelector = @selector(viewDidLoad);
        SEL swizzledSelector = @selector(jkviewDidLoad);
        
        Method originalMethod = class_getInstanceMethod(class,originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
        
        //judge the method named  swizzledMethod is already existed.
        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        // if swizzledMethod is already existed.
        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }
        else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)jkviewDidLoad {
    NSLog(@"替換的方法");
    
    [self jkviewDidLoad];
}

- (void)viewDidLoad {
    NSLog(@"自帶的方法");
    
    [super viewDidLoad];
}

@end
實現(xiàn)圖片詳解

例子2:友盟統(tǒng)計埋點時候交換系統(tǒng)方法

友盟的方法照著文檔寫就可以洛搀;runtime那個就是交換系統(tǒng)的方法達到無侵入的埋點。主要看要交換的方法佑淀,比如最常見的vc的那幾個周期方法留美。無侵入的意思就是不要到處都去寫。

例子3:修改重寫 KVC 的 undefinedKey 方法

KVC 鍵值對的時候 key 找不到 vaue 的時候就會調用 undefinedKey 方法伸刃,就無法字典轉模型谎砾,這個時候可以利用 Runtime 修改重寫這個 undefinedKey 方法解決奔潰,就可以繼續(xù)使用 KVC 進行字典轉模型來使用捧颅。

  • 2.3 KVO 實現(xiàn)

全稱是 Key-value observing景图,翻譯成鍵值觀察。提供了一種當其它對象屬性被修改的時候能通知當前對象的機制碉哑,在 MVC 大行其道的 Cocoa 中挚币,KVO 機制很適合實現(xiàn) model 和 controller 類之間的通訊亮蒋。

KVO 實現(xiàn)詳解1
KVO 實現(xiàn)詳解2

3.weak 釋放 nil 的過程

Runtime 會對 weak 屬性進行內存布局,構建 hash 表:以 weak 屬性對象內存地址為 key妆毕,weak 屬性值(weak自身地址)為 value慎玖。當對象引用計數(shù)為0 dealloc 時,會將 weak 屬性值自動置 nil笛粘。(Hash 表你理解成字典就行)

Runtime 如何實現(xiàn) weak 屬性
  • 補充:weak 屬性需要在 dealloc 中置 nil 么凄吏?

4.消息轉發(fā)(熱更新)解決 Bug(JSPatch)

JSPatch 是一個 iOS 動態(tài)更新框架,只需在項目中引入極小的引擎闰蛔,就可以使用 JavaScript 調用任何 Objective-C 原生接口,獲得腳本語言的優(yōu)勢:為項目動態(tài)添加模塊或替換項目原生代碼動態(tài)修復 bug图柏。

關于消息轉發(fā)序六,前面已經(jīng)講到過了,消息轉發(fā)分為三級蚤吹,我們可以在每級實現(xiàn)替換功能例诀,實現(xiàn)消息轉發(fā),從而不會造成崩潰裁着。JSPatch 不僅能夠實現(xiàn)消息轉發(fā)繁涂,還可以實現(xiàn)方法添加、替換等一系列功能二驰。

5.實現(xiàn) NSCoding 的自動歸檔和自動解檔

原理描述:用 Runtime 提供的函數(shù)遍歷 Model 自身所有屬性扔罪,并對屬性進行 encode 和 decode 操作。 核心方法為在 Model 的基類中重寫方法桶雀,代碼如下矿酵。

- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        unsigned int outCount;
        Ivar * ivars = class_copyIvarList([self class], &outCount);
        for (int i = 0; i < outCount; i ++) {
            Ivar ivar = ivars[i];
            NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
        }
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
    unsigned int outCount;
    Ivar * ivars = class_copyIvarList([self class], &outCount);
    for (int i = 0; i < outCount; i ++) {
        Ivar ivar = ivars[i];
        NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
}

6.實現(xiàn)字典和模型的自動轉換(MJExtension)

原理描述:用 Runtime 提供的函數(shù)遍歷 Model 自身所有屬性,如果屬性在 json 中有對應的值矗积,則將其賦值全肮。 核心方法為在 NSObject 的分類中添加方法,代碼如下棘捣。

- (instancetype)initWithDict:(NSDictionary *)dict {

    if (self = [self init]) {
        //(1)獲取類的屬性及屬性對應的類型
        NSMutableArray * keys = [NSMutableArray array];
        NSMutableArray * attributes = [NSMutableArray array];
        /*
         * 例子
         * name = value3 attribute = T@"NSString",C,N,V_value3
         * name = value4 attribute = T^i,N,V_value4
         */
        unsigned int outCount;
        objc_property_t * properties = class_copyPropertyList([self class], &outCount);
        for (int i = 0; i < outCount; i ++) {
            objc_property_t property = properties[i];
            //通過property_getName函數(shù)獲得屬性的名字
            NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
            [keys addObject:propertyName];
            //通過property_getAttributes函數(shù)可以獲得屬性的名字和@encode編碼
            NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
            [attributes addObject:propertyAttribute];
        }
        //立即釋放properties指向的內存
        free(properties);

        //(2)根據(jù)類型給屬性賦值
        for (NSString * key in keys) {
            if ([dict valueForKey:key] == nil) continue;
            [self setValue:[dict valueForKey:key] forKey:key];
        }
    }
    return self;

}

7.[self class] 和 [super class]

https://blog.csdn.net/qq_45836906/article/details/119176086
https://juejin.cn/post/6844904100228759560
https://juejin.cn/post/6844904100228759560

8.Runtime補充說明

其實 KVC 就是 Runtime辜腺,因為 KVC 的賦值取值都是在運行時操作。

ios 中 Runtime 介紹以及使用:
http://www.reibang.com/p/595e4c63c732
http://www.reibang.com/p/add581f0e8bb#

作為一門動態(tài)編程語言乍恐,Objective-C 會盡可能的將編譯(動態(tài)加載)和鏈接(動態(tài)綁定)時要做的事情推遲到運行時评疗。這意味著 Objective-C 語言不僅需要一個編譯環(huán)境,同時也需要一個運行時系統(tǒng)來執(zhí)行編譯好的代碼禁熏。運行時它是蘋果提供的純C語言的開發(fā)庫很優(yōu)秀壤巷。運行時的作用如圖

例子 - 運行時獲取成員變量名稱

#import <Foundation/Foundation.h>
#import "XMGPerson.h"
#import <objc/runtime.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // 成員變量的數(shù)量
        unsigned int outCount = 0;

        // 獲得所有的成員變量
        // ivars是一個指向成員變量的指針
        // ivars默認指向第0個成員變量(最前面)
        Ivar *ivars = class_copyIvarList([XMGPerson class], &outCount);

        // 遍歷所有的成員變量
        for (int i = 0; i<outCount; i++) {
            // 取出i位置對應的成員變量
//            Ivar ivar = *(ivars + i);
            Ivar ivar = ivars[i];
            // 獲得成員變量的名字
            NSLog(@"%s", ivar_getName(ivar));
        }

        // 如果函數(shù)名中包含了copy\new\retain\create等字眼,那么這個函數(shù)返回的數(shù)據(jù)就需要手動釋放
        free(ivars);

//        Ivar ivar = *ivars;
//        Ivar ivar2 = *(ivars + 1);
//        NSLog(@"%s %s", ivar_getName(ivar), ivar_getName(ivar2));


        // 一個Ivar就代表一個成員變量

        // int *p; 指向int類型的變量
        // Ivar *ivars; 指向Ivar類型的變量
    }
    return 0;
}
獲取UITextFiled成員變量的名稱
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末瞧毙,一起剝皮案震驚了整個濱河市胧华,隨后出現(xiàn)的幾起案子寄症,更是在濱河造成了極大的恐慌,老刑警劉巖矩动,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件有巧,死亡現(xiàn)場離奇詭異,居然都是意外死亡悲没,警方通過查閱死者的電腦和手機篮迎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來示姿,“玉大人甜橱,你說我怎么就攤上這事≌淮粒” “怎么了岂傲?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長子檀。 經(jīng)常有香客問我镊掖,道長,這世上最難降的妖魔是什么褂痰? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任亩进,我火速辦了婚禮,結果婚禮上缩歪,老公的妹妹穿的比我還像新娘归薛。我一直安慰自己,他們只是感情好匪蝙,可當我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布苟翻。 她就那樣靜靜地躺著,像睡著了一般骗污。 火紅的嫁衣襯著肌膚如雪崇猫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天需忿,我揣著相機與錄音诅炉,去河邊找鬼。 笑死屋厘,一個胖子當著我的面吹牛涕烧,可吹牛的內容都是我干的。 我是一名探鬼主播汗洒,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼议纯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了溢谤?” 一聲冷哼從身側響起瞻凤,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤憨攒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后阀参,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肝集,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年蛛壳,在試婚紗的時候發(fā)現(xiàn)自己被綠了杏瞻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡衙荐,死狀恐怖捞挥,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情忧吟,我是刑警寧澤树肃,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站瀑罗,受9級特大地震影響,放射性物質發(fā)生泄漏雏掠。R本人自食惡果不足惜斩祭,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望乡话。 院中可真熱鬧摧玫,春花似錦、人聲如沸绑青。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽闸婴。三九已至坏挠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間邪乍,已是汗流浹背降狠。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留庇楞,地道東北人榜配。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像吕晌,于是被迫代替她去往敵國和親蛋褥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內容

  • 運行時的文章一直被同學們熱炒睛驳,當然現(xiàn)在面試中也都喜歡問道烙心,當大伙說的頭頭是道時候膜廊,可到真正的項目中幾乎局限只會關聯(lián)...
    iOS軟件學習閱讀 397評論 0 1
  • 運行時的文章一直被同學們熱炒,當然現(xiàn)在面試中也都喜歡問道弃理,當大伙說的頭頭是道時候溃论,可到真正的項目中幾乎局限只會關聯(lián)...
    ios設計閱讀 514評論 1 2
  • 運行時的文章一直被同學們熱炒,當然現(xiàn)在面試中也都喜歡問道痘昌,當大伙說的頭頭是道時候钥勋,可到真正的項目中幾乎局限只會關聯(lián)...
    ios教程閱讀 633評論 2 4
  • 運行時的文章一直被同學們熱炒,當然現(xiàn)在面試中也都喜歡問道辆苔,當大伙說的頭頭是道時候算灸,可到真正的項目中幾乎局限只會關聯(lián)...
    _羊羽_閱讀 3,022評論 1 43
  • 于是我總結了很多網(wǎng)上被問到的一些關于runtime的題目,并做了詳細的回答驻啤,并在后面補充了我在學習runtime時...
    Sky109閱讀 507評論 0 2