runtime是運行時庫(Runtime Library)商虐,也簡稱運行時何荚。
它是一個主要是C和匯編寫的庫,對C進行了特殊的處理剃毒,將結(jié)構(gòu)體視為對象,將函數(shù)視為方法搂赋,使得C有了面向?qū)ο蟮哪芰ψ阜В瑥亩艅?chuàng)造了Objective-C。
這點也可以看出脑奠,C是編譯時語言基公,而OC是動態(tài)運行時語言,所以在編譯階段宋欺,盡管OC中的方法沒有實現(xiàn)也不會報錯轰豆,而C會報錯。
在運行時齿诞,OC語言才進行方法的處理酸休,比如講[person eat];轉(zhuǎn)換為objc_msgSend(person, @selector(eat));然后通過person的isa指針找到person對應(yīng)的class,在class中先去cache中通過SEL方法選擇器查找對應(yīng)的method掌挚,若緩存中未找到雨席,再去methodList查找,若還未找到吠式,便去父類中查找陡厘,如果這個過程中找到了該方法,便會自動將該方法加入cache中特占,方便下一次的查找糙置,并且,編譯器開始執(zhí)行找到的這個函數(shù)是目。
上面這段文字可以片面的理解runtime的消息機制谤饭,消息機制是runtime最主要的機制,如果要在代碼中使用懊纳,需要導(dǎo)入揉抵。
首先,對objc_class結(jié)構(gòu)體的內(nèi)容做個簡單的說明:
[objc]view plaincopy
structobjc_class?{
Class?isa??OBJC_ISA_AVAILABILITY;
#if?!__OBJC2__
Class?super_class//指向父類
constcharchar*name//類名
longversion//類的版本信息(默認是0嗤疯,class_setVersion,clss_getVersion可set和get)
longinfo//類的標識信息
longinstance_size//該類的實例變量size
structobjc_ivar_list*ivars//用于存儲每個成員變量的地址
structobjc_method_list**methodLists//與info的標識信息有關(guān)冤今,如CLS_CLASS存儲實例方法,CLS_META存儲靜態(tài)方法
structobjc_cache*cache//指向最近使用方法的指針茂缚,它的作用是提升效率戏罢,可以不嚴密的理解為緩存
structobjc_protocol_list*protocols//存儲協(xié)議列表
#endif
}?OBJC2_UNAVAILABLE;
在實際開發(fā)中屋谭,runtime最常用的場景就是交換方法、歸檔龟糕、字典轉(zhuǎn)模型桐磁、動態(tài)生成屬性方法、添加類方法等
1.創(chuàng)建一個類的分類
2.導(dǎo)入
3.實現(xiàn)交換并且自定義方法
[objc]view plaincopy
//由于交換方法我們希望在工程運行期間只執(zhí)行一次讲岁,所以通常寫在load里面
+?(void)load?{
Method?imageMethodNamed?=?class_getClassMethod(self,@selector(imageNamed:));
Method?myImageMethodNamed?=?class_getClassMethod(self,@selector(myImageNamed:));
//實現(xiàn)兩個方法的交換
method_exchangeImplementations(myImageMethodNamed,imageMethodNamed);
}
+?(instancetype)customImageNamed:(NSString*)name?{
//實際調(diào)用的是imageNamed:
UIImage*image?=?[UIImagecustomImageNamed:name];
//*此處為自定義配置*
returnimage;
}
(二)runtime動態(tài)生成getter我擂、setter
由于在類目中添加屬性不自動生成setter和getter方法,如果在.m文件中使用@dynamic配置起來就較為繁瑣催首,如果一定要在類目中添加屬性扶踊,我們可以使用runtime關(guān)聯(lián)方法,簡單方便郎任。
比如在類目.h文件中聲明了score屬性,在.m文件中如下實現(xiàn):
[objc]view plaincopy
voidvoid*key;
-?(void)setScore:(float)score?{
//關(guān)聯(lián)引用
/**
*??1.給哪個對象屬性進行關(guān)聯(lián)
*??2.用來保存?zhèn)魅氲闹档闹羔?用于get方法獲取值)
*??3.傳入的值(注意是對象類型)
*??4.關(guān)聯(lián)引用的策略(這個根據(jù)屬性添加的修飾而定)
*/
objc_setAssociatedObject(self,?key,?@(score),?OBJC_ASSOCIATION_ASSIGN);
}
-?(float)score?{
idscore?=?objc_getAssociatedObject(self,?key);
return[scorefloatValue];
}
歸解檔可以用kvc模式進行操作舶治,但是如果屬性過多的話,這樣就顯得極為繁瑣而且容易出錯车猬,所以用runtime實現(xiàn)更為的科學(xué)和簡潔霉猛。
[objc]view plaincopy
//歸檔
-?(void)encodeWithCoder:(NSCoder*)aCoder?{
//獲取某個類的所有成員變量
unsignedintcount?=0;
Ivar*ivarList?=?class_copyIvarList([selfclass],?&count);
//歸檔
for(inti?=0;?i?<?count;?i?++)?{
Ivar?aIvar?=?ivarList[i];
//獲取成員變量的名稱
constcharchar*iVarName?=?ivar_getName(aIvar);
idvalue?=?[selfvalueForKey:[NSStringstringWithUTF8String:iVarName]];
if(!value)?{
}else{
[aCoderencodeObject:valueforKey:[NSStringstringWithUTF8String:iVarName]];
}
}
}
//解檔
-?(instancetype)initWithCoder:(NSCoder*)aDecoder?{
if(self=?[superinit])?{
unsignedintcount?=0;
Ivar*ivarList?=?class_copyIvarList([selfclass],?&count);
for(inti?=0;?i?<?count;?i?++)?{
Ivar?aIvar?=?ivarList[i];
constcharchar*name?=?ivar_getName(aIvar);
idvalue?=?[aDecoderdecodeObjectForKey:[NSStringstringWithUTF8String:name]];
if(!value)?{
}else{
[selfsetValue:valueforKey:[NSStringstringWithUTF8String:name]];
}
}
}
returnself;
}
這里需要注意的是,該方法只適用于字典的鍵和模型的屬性一一對應(yīng)的情況珠闰,如果要處理不一一對應(yīng)的情況惜浅,最簡單的解決方法是使用三方。
[objc]view plaincopy
+?(instancetype)modelWithDictionary:(NSDictionary*)dic?{
Student*aStudent?=?[Studentnew];
unsignedintcount?=0;
objc_property_t*propertyList?=?class_copyPropertyList([selfclass],?&count);
for(inti?=0;?i?<?count;?i?++)?{
objc_property_t?aProperty?=?propertyList[i];
//獲取屬性名
constcharchar*name?=?property_getName(aProperty);
idvalue?=?dic[[NSStringstringWithUTF8String:name]];
if(!value)?{
}else{
//使用kvc給屬性賦值
[aStudentsetValue:valueforKey:[NSStringstringWithUTF8String:name]];
}
}
returnaStudent;
}
[objc]view plaincopy
voidstudy(idreccevier,SELsel)?{
}
//?如果調(diào)用的方法沒有實現(xiàn)伏嗜,就會走這個方法
+?(BOOL)resolveInstanceMethod:(SEL)sel?{
if(sel?==@selector(study))?{
class_addMethod(self,@selector(study),?(IMP)study,"v@:");
}
return[superresolveInstanceMethod:sel];
}
值得注意的是坛悉,這個方法是在運行時動態(tài)調(diào)用的,所以編譯的時候會有警告承绸。