文章分享至我的個人技術部落格:https://cainluo.github.io/15074742481003.html
在上一章節裡曉得了怎麼在Category
裡關聯物件, 以及利用RunTime
轉換模型的時候預防了三種轉換時的情況, 如果沒有去看的朋友可以到玩轉iOS開發:裝逼技術RunTime的應用(二)看看.
轉載宣告:如需要轉載該文章, 請聯絡作者, 並且註明出處, 以及不能擅自修改本文.
利用Runtime歸檔
在以前我們在使用歸檔的時候都會有一個煩惱, 就是寫的太多, 不信? 我們來宣告一個物件:
#import <Foundation/Foundation.h>
@interface RunTimeCoderModel : NSObject <NSCoding>
@property (nonatomic, copy) NSString *cl_name;
@property (nonatomic, copy) NSString *cl_height;
@property (nonatomic, copy) NSString *cl_age;
@end
複製程式碼
常規歸檔的寫法:
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_cl_name
forKey:@"name"];
[aCoder encodeObject:_cl_height
forKey:@"height"];
[aCoder encodeObject:_cl_age
forKey:@"age"];
}
複製程式碼
常規解檔的寫法:
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
self.cl_name = [aDecoder decodeObjectForKey:@"name"];
self.cl_height = [aDecoder decodeObjectForKey:@"height"];
self.cl_age = [aDecoder decodeObjectForKey:@"age"];
}
return self;
}
複製程式碼
現在看著好像也不怎麼樣, 但在實際開發中, 我們要寫的屬性可不是隻有這三個, 如果遇到變態的, 有上百個那怎麼辦呢?
逐個逐個去寫麼? 萬一寫完之後突然要改屬性怎麼辦? 逐個去改? 這樣子就會大量的浪費我們的時間, 這是不明智的寫法.
回想一下, 每個類都有一個isa
的結構體指標, 裡面可以拿到所有的每個類的資訊, 那我們是否可以通過這個特性, 來給歸檔解檔操作一番呢? 試試看:
RunTime
歸檔的寫法:
- (void)cl_runtimeEncoderWithCoder:(NSCoder *)coder {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[coder encodeObject:value
forKey:key];
}
free(ivarList);
}
複製程式碼
RunTime
解檔寫法:
- (void)cl_runtimeDecideWithCoder:(NSCoder *)decoder {
unsigned int count = 0;
Ivar *ivarList = class_copyIvarList([self class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = ivarList[i];
const char *name = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:name];
id value = [decoder decodeObjectForKey:key];
[self setValue:value
forKey:key];
}
free(ivarList);
}
複製程式碼
最終的使用:
- (void)encodeWithCoder:(NSCoder *)aCoder {
[self cl_runtimeEncoderWithCoder:aCoder];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self) {
[self cl_runtimeDecideWithCoder:aDecoder];
}
return self;
}
複製程式碼
最終的效果:
![1](https://i.iter01.com/images/1730318472026c048ca27c9bb3f0878224bbd251d26417b032845936bb2d6a73.png)
這的確是可行的, 這樣子我們就把這個寫成一個通用的類, 並且遵守<NSCoding>
協議, 就可以把所有繼承與NSObject
的類全部一次性歸檔.
在這裡我就不對歸檔和解檔的方法進行封裝了, 都寫在RunTimeCoderController
這個控制器上, 有想法的朋友可以自行進行封裝, 這樣子就可以抽成一個通用類.
RunTime黑魔法
前段時間搜了一下關於RunTime
的一些部落格, 發現有很多人都說RunTime
黑魔法, 那什麼是黑魔法?
- 簡單的來說, 其實就是進行方法交換.
- 我們都知道, 在
Objective-C
中呼叫一個方法, 其實是向一個物件傳送訊息, 而查詢訊息的唯一依據就是selector
的名字, 利用Objective-C
的動態特性, 我們可以實現在執行時偷偷的換掉selector
對應的方法實現. - 而我們也逗知道, 每一個類都有一個方法列表, 存放著方法的名字和方法實現的對映關係,
selector
其實就是方法名, 而IMP
類似函式指標, 指向具體的Method
實現, 通過selector
就可以找到對應的IMP
.
![2](https://i.iter01.com/images/73fd7b8c29ddeaa60abb7b9d28f981eb03ad45f3d24b59c88f0549bbf1bb3436.png)
- 交換方法的實現方式
- 利用
method_exchangeImplementations
來交換兩個方法的實現 - 利用
class_replaceMethod
替換方法的實現 - 利用
method_setImplementation
來直接設定某個方法的IMP
- 利用
![3](https://i.iter01.com/images/84c41080cebe75c6fe1f0248b798f0aae4b49af99ae8c010776343917b4627d7.png)
除了我們在演示裡寫過的程式碼, 在實際上又是怎麼運用呢? 這裡收集到了幾種場景:
- 替換
ViewController
的生命週期方法 - 解決集合類的獲取索引, 新增, 刪除元素越界崩潰的問題
- 防止按鈕重複暴力點選
- 全域性更換控制元件初始化的效果
- App的熱修復
- App空資料時的佔點陣圖工具類封裝
- 全域性修改
UINavigationBar
的BackButtonItem
程式碼這裡就不寫了, 想詳細瞭解的朋友可以到下面的文章去了解.
Runtime Method Swizzling開發例項彙總
推薦文章
runtime 完整總結 objc_msgSend runtime詳解 讓你快速上手Runtime 利用Runtime 實現自動化歸檔 runtime那些事(訊息機制) OC最實用的runtime總結,面試、工作你看我就足夠了! Runtime在實際開發中的應用
工程地址
專案地址: https://github.com/CainRun/iOS-Project-Example/tree/master/RunTime/Five
最後
![微信](https://i.iter01.com/images/9899f30700b52ddd163180af67bfdbbe3cf6c9c86ede15fbf169acedb39723e5.jpg)
![支付寶](https://i.iter01.com/images/a6e7bac6ebcc4cf2ccf6f004ade1b43f3698e71e0b519996a4cf18eecb038cf3.jpg)