1.0 Runtime介紹
- 1.1 Runtime簡介
因為Objc是一門動態(tài)語言,所以它總是想辦法把一些決定工作從編譯連接推遲到運行時。
也就是說只有編譯器是不夠的,還需要一個運行時系統(tǒng) (runtime system) 來執(zhí)行編譯后的代碼。
這就是 Objective-C Runtime 系統(tǒng)存在的意義,它是整個Objc運行框架的一塊基石。
Runtime基本是用C和匯編寫的,可見蘋果為了動態(tài)系統(tǒng)的高效而作出的努力。
Runtime其實有兩個版本:“modern”和 “l(fā)egacy”。
1.我們現(xiàn)在用的 Objective-C 2.0 采用的是現(xiàn)行(Modern)版的Runtime系統(tǒng),只能運行在 iOS 和 OS X 10.5 之后的64位程序中。
2.而OS X較老的32位程序仍采用 Objective-C 1中的(早期)Legacy 版本的 Runtime 系統(tǒng)。
3.這兩個版本最大的區(qū)別在于當你更改一個類的實例變量的布局時,在早期版本中你需要重新編譯它的子類,而現(xiàn)行版就不需要。
4.蘋果和GNU各自維護一個開源的runtime版本,這兩個版本之間都在努力的保持一致。
- 1.2 Runtime總結(jié)
1.OC就是運行時機制
2.Runtime是消息機制
3.OC方法調(diào)用的本質(zhì)就是發(fā)送消息
- 1.3 Objective-C 從三種不同的層級上與 Runtime 系統(tǒng)進行交互
1.分別是通過 Objective-C 源代碼
2.通過 Foundation 框架的NSObject類定義的方法
3.通過對 runtime 函數(shù)的直接調(diào)用。
2.0 Runtime使用
- 2.1 Runtime作用
總結(jié)起來無非是:
可以在運行時,在不繼承也不category的情況下,為各種類(包括系統(tǒng)的類)做很多操作,具體包括:
增加
1.增加函數(shù):class_addMethod
2.增加實例變量:class_addIvar
3.增加屬性:@dynamic標簽,或者class_addMethod,因為屬性其實就是由getter和setter函數(shù)組成
4.增加Protocol:class_addProtocol (說實話我真不知道動態(tài)增加一個protocol有什么用,-_-!!)
獲取
1.獲取函數(shù)列表及每個函數(shù)的信息(函數(shù)指針、函數(shù)名等等):class_getClassMethod method_getName ...
2.獲取屬性列表及每個屬性的信息:class_copyPropertyList property_getName
3.獲取類本身的信息,如類名等:class_getName class_getInstanceSize
4.獲取變量列表及變量信息:class_copyIvarList
5.獲取變量的值
替換:
1.將實例替換成另一個類:object_setClass
2.將函數(shù)替換成一個函數(shù)實現(xiàn):class_replaceMethod
3.直接通過char *格式的名稱來修改變量的值,而不是通過變量
- 2.2 Runtime使用細節(jié)
1.使用Runtime則必須導(dǎo)入
#import <objc/message.h> 或者 #import<objc/runtime.h>
// 前者包含后者
2. Runtime函數(shù)的命名規(guī)則: 誰的事情,誰開頭
例如: objc_getClass:獲取類對象
objc_getMetaClass:獲取元類
3.0 用到的OC常識
- 3.1 OC常識
1. OC對象方法都保存在類對象中方法列表中,OC對象中類方法保存在元類(meta)
2. OC中所有方法最終都會轉(zhuǎn)換成函數(shù)
3. 內(nèi)存五大區(qū):棧,堆,常量區(qū),全局區(qū),方法區(qū)
a. 棧:自動管理內(nèi)存
b. 堆:手動管理內(nèi)存
- 3.2 OC方法調(diào)用流程:
1.根據(jù)對象的isa去對應(yīng)類對象去查找方法, isa:指向類對象
2.根據(jù)傳入方法編號,去類對象中方法列表中查找對應(yīng)方法
3.調(diào)用方法實現(xiàn)

4.0 Runtime常用方法示例
- 4.1 Runtime(消息機制)
- 首先,你需要知道這兩個概念:
1.OC中調(diào)用方法就是向?qū)ο蟀l(fā)送消息。
比如 :[person run];
這實際上這是在給person這個對象發(fā)送run這個消息。
2.當run這個方法只有定義沒有實現(xiàn)就會報錯:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person run]: unrecognized selector sent to instance
經(jīng)典消息發(fā)送
消息轉(zhuǎn)發(fā)
-
4.2 獲取類的所有方法列表
獲取到的數(shù)據(jù)是一個Method數(shù)組,Method數(shù)據(jù)結(jié)構(gòu)中包含了函數(shù)的名稱、參數(shù)、返回值等信息,以下代碼以獲取名稱為例:
u_int count;
Method * methods= class_copyMethodList([UIView class], &count);
for (int i = 0; i < count ; i++)
{
SEL name = method_getName(methods[i]);
NSString *strName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
NSLog(@"%@",strName);
}
- 4.3 Runtime(交換方法)
- 交換方法實現(xiàn) : 調(diào)用A的方法名但是會去執(zhí)行B的方法.
- ###### !!!友情提醒 : 不要為了使用runtime,而去使用(超級容易坑隊友)
- 使用的一些場景 :
-
1.給系統(tǒng)的方法增加功能
-
例如 : 給UIImage的imageNamed方法提供一個功能,加載圖片的時候,會告訴開發(fā)者是否加載功能
//TODO: 在原來加載圖片功能之上,在添加一個判斷是否加載成功功能
1.自定義類,重寫系統(tǒng)方法 弊端:1.每次使用,都需要導(dǎo)入
2.給UIImage提供分類,擴充方法 弊端:1.每次使用,都需要導(dǎo)入
tips: 2.1 在分類中,重寫系統(tǒng)方法實現(xiàn),會導(dǎo)致系統(tǒng)方法被干掉
2.2 在分類中,重寫系統(tǒng)方法實現(xiàn),容易坑到隊友,慎用
3.交換方法 交換imageNamed和bl_imageNamed實現(xiàn)
3.1 根據(jù)OC方法調(diào)用的流程,我們只需要交換方法列表映射就可以實現(xiàn)方法交換

Runtime代碼實現(xiàn)方法交換 :
ViewController.m 文件
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// bl_imageNamed有加載圖片和判斷功能
[UIImage imageNamed:@"123"];
}
UIImage+Image.m 文件
import "UIImage+Image.h"
import <objc/message.h>
@implementation UIImage (Image)
/*load方法特性:
1.程序一開始啟動,就會把所有類加載進內(nèi)存,此時就會調(diào)用load方法
2.只會調(diào)用一次
*/
-
(void)load
{
// 所有方法都保存在類中
// Class:獲取哪個類方法 [xxxx class]
// SEL:獲取哪個方法Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
Method bl_imageNameMethod = class_getClassMethod(self, @selector(bl_imageNamed:));// 交換imageNamed和bl_imageNamed實現(xiàn)
method_exchangeImplementations(imageNameMethod, bl_imageNameMethod);
} -
(UIImage *)bl_imageNamed:(NSString *)name
{
// 1.加載圖片
// 2.由于交換了映射 :在bl_imageNamed方法內(nèi)部調(diào)用bl_imageNamed,實際是調(diào)用了imageNamed
UIImage *image = [UIImage bl_imageNamed:name];// 3.判斷是否加載成功
if (image == nil) {
NSLog(@"加載失敗");
} else {
NSLog(@"加載成功");
}
return image;
}
- TODO : Runtime(動態(tài)添加方法)
留坑
- TODO :Runtime(動態(tài)添加屬性)
留坑
- ### Runtime 一些常見的問題與解決方法
Q1 : 怎么才能讓消息機制函數(shù)有參數(shù)提示?
A1 : runtime的消息機制函數(shù),在xcode6之后就沒有提示參數(shù)
S1 : 點擊工程文件 -> build Setting -> 搜索msg -> Enable Strict Checking of objc_msgSend Calls(不要嚴肅檢查消息機制調(diào)用) -> NO
OC_runtime運行時官方文檔翻譯:http://www.ithao123.cn/content-801906.html