.概念
RunTime顧名思義是指運行的時候,簡稱運行時,OC就是運行時機制,其中最主要的是消息機制。對于C語言,函數(shù)的調(diào)用在編譯的時候會決定調(diào)用哪個函數(shù)。對于OC的函數(shù),屬于動態(tài)調(diào)用過程,在編譯的時候并不能決定真正調(diào)用哪個函數(shù),只有在真正運行的時候才會根據(jù)函數(shù)的名稱找到對應(yīng)的函數(shù)來調(diào)用,
Runtime是蘋果用C和匯編寫的一個庫,所以在用RunTime的時候,我們寫的事C語言的語法,而不是OC。
RunTime的意義:
因為Objc是一門動態(tài)語言,所以它總是想辦法把一些決定工作從編譯連接推遲到運行時。也就是說只有編譯器是不夠的,還需要一個運行時系統(tǒng)(runtime
system) 來執(zhí)行編譯后的代碼。這就是 Objective-CRuntime 系統(tǒng)存在的意義,它是整個Objc運行框架的一塊基石。
2.結(jié)構(gòu)解讀
使用時,需要導入關(guān)鍵文件:
#import
#import
message.h中主要包含了一些向?qū)ο蟀l(fā)送消息的函數(shù),不如下面這個api,就是消息發(fā)送的兩個方法
runtime.h是運行時最重要的文件,其中包含了基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)和對運行時進行操作的方法。
圖1:
在runtime.h里面,主要的內(nèi)容有
類型的定義,如:
objc_method *Method;//類中的一個方法
struct objc_ivar *Ivar;//實例(對象)的變量
......
函數(shù)的定義,說明:
對對象進行操作的方法一般以object_開頭
對類進行操作的方法一般以class_開頭
對類或?qū)ο蟮姆椒ㄟM行操作的方法一般以method_開頭
對成員變量進行操作的方法一般以ivar_開頭
對屬性進行操作的方法一般以property_開頭開頭
對協(xié)議進行操作的方法一般以protocol_開頭
.......
主要應(yīng)用
1.消息分發(fā)(方法調(diào)用)
例子1:
//創(chuàng)建person對象
Person *p = [[Person alloc] init];
[p eat];
上面我們創(chuàng)建了一個Person對象p,并讓他去執(zhí)行 eat方法;在運行時(程序跑到這里)的時候,編譯器會把 [p eat]; 轉(zhuǎn)換成objc_msgSend(p,@selector(eat),
這個函數(shù)完成了動態(tài)綁定的所有事情:
首先它找到selector對應(yīng)的方法實現(xiàn)。因為同一個方法可能在不同的類中有不同的實現(xiàn),所以我們需要依賴于接收者的類來找到的確切的實現(xiàn)。找方法實現(xiàn)其實就是找到該方法指針(IMP)所指的地址。
它調(diào)用方法實現(xiàn),并將接收者對象及方法的所有參數(shù)傳給它。
最后,它將實現(xiàn)返回的值作為它自己的返回值。
消息的關(guān)鍵在于我們上面說過的的結(jié)構(gòu)體objc_class,這個結(jié)構(gòu)體有兩個字段是我們在分發(fā)消息時所用到的:
指向父類的指針 isa (屬于誰)
一個類的方法分發(fā)表,即methodLists(方法查找)。
2.動態(tài)綁定
動態(tài)綁定是runtime 最重要的一個特性,其包括動態(tài)增加屬性,動態(tài)增加方法,動態(tài)交換方法的實現(xiàn)...,靈活多變
動態(tài)給對象添加屬性值
runtime里的api如下:
objc_setAssociatedObject(idobject,const void*key,idvalue,objc_AssociationPolicypolicy)//新增一個屬性
idobjc_getAssociatedObject(idobject,constvoid*key) //取屬性值
這里的object就是你要操作的對象(給誰添加),key是唯一的,必須為常量,policy是內(nèi)存操作的類型,如下面:
例子2:
-(void)setchild:(id)child
{
objc_setAssociatedObject(self,@selector(child),child,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(id)child
{
returnobjc_getAssociatedObject(self,_cmd);
}
在例子2中,我們?yōu)閷ο筇砑恿艘粋€child的屬性,通過self.child就可以對child進行賦值和取值,這個方法可以在任何地方,任何時候使用。
動態(tài)給對象添加方法
runtime里主要的api如下:
BOOLclass_addMethod(Class cls,SELname,IMPimp,
constchar*types)
cls 就是你要操作的對象,name就是方法名,imp是方法實現(xiàn)的指針,types是參數(shù)類型
例子3
class_addMethod(man.class,NSSelectorFromString(@"addkongfu"),(IMP)leanKongfu,"@:");//給對象添加一個方法
}
NSString* leanKongfu(People *man ,SEL_cmd,NSString*name)
{
NSString*des = [NSString stringWithFormat:@"%@ 學了 %@",man.name,name];
returndes;
}
上面的例子3中,我們給對象man添加了一個addkongfu的方法,方法的指針指向leanKongfu,用如下代碼可以調(diào)用動態(tài)添加的方法:
[selfperformSelectorInBackground:@selector(addkongfu)withObject:@"葵花寶典"];//調(diào)用addkongfu,并傳入?yún)?shù)@"葵花寶典"。
動態(tài)獲取屬性列表和方法列表,改變屬性值
獲取屬性列表,這才json數(shù)據(jù)解析與封裝中起到至關(guān)重要的作用,如果連一個結(jié)構(gòu)有什么屬性都不知道,怎么賦值?
圖2
再看objc_class的結(jié)構(gòu)圖,里面存在4個鏈表結(jié)構(gòu),分別是,屬性對象鏈表ivars,方法鏈表methodLists,緩存鏈表cache,OC聲明的屬性類型鏈表protocals,由名字我們就可以知道一個對象的結(jié)構(gòu)數(shù)據(jù)的存放位置了。runtime提供了獲取各種數(shù)據(jù)的接口
主要方法:
int count;
Ivar*?ivars = class_copyIvarList(self.class,&count)//獲取結(jié)構(gòu)體
for(inti= 0; i
{
Ivarivar =?ivars[i];
char* name =? ivar_getName(ivar);獲取成員變量名
char* type = ivar_getTypeEncoding(ivar);//獲取成員變量屬性
}
通過上面的代碼,我們就可以獲得一個class里面所有的成員變量名和相應(yīng)的類型,如下面的例子4:
由于runtime是用C語言寫的庫,不支持ARC的自動回收,所以在用完之后要手動去清理內(nèi)存,free();