runtime ,小伙伴們在面試的會經(jīng)常問到,runtime 里面的獲取目標類的類名,方法名等方法一般是封裝一般類共用方法的基礎。所以我們有必要理清運行時到底是什么。它有什么用途,即方法用途。
程序員有時候會在什么東西應該在編譯的時候加載進來以及什么東西該在運行的時候使用之間做出抉擇,前者有時候被稱為編譯時期。
一段時間以來,技術(shù)類作者都拒絕使用"運行時刻"作為一種術(shù)語,他們堅持類似于"一個程序在運行"之類的說法,用以避免需要一個專門的術(shù)語。后來,這個術(shù)語逐漸地蔓延到通常的應用中。Objective-C Runtime 是開源的
Objective-C 是開源的,任何時候你都能從 http://opensource.apple.com. 獲取。事實上查看 Objective-C 源碼是我理解它是如何工作的第一種方式,在這個問題上要比讀蘋果的文檔要好。你可以下載適合 Mac OS X 10.6.2 的 objc4-437.1.tar.gz。(譯注:最新objc4-551.1.tar.gz)
Objective-C 是面相運行時的語言(runtime oriented language),就是說它會盡可能的把編譯和鏈接時要執(zhí)行的邏輯延遲到運行時。這就給了你很大的靈活性,你可以按需要把消息重定向給合適的對象,你甚 至可以交換方法的實現(xiàn),等等(譯注:在 Objective-C 中調(diào)用一個對象的方法可以看成向一個對象發(fā)送消息, Method Swizzling 具體實現(xiàn)可以參看 jrswizzle )。這就需要使用 runtime,runtime 可以做對象自省查看他們正在做的和不能做的(don't respond to)并且合適的分發(fā)消息(譯注:感興趣的同學可以查看 NSObject 類的 – forwardingTargetForSelector: 和 – forwardInvocation: 方法。P.S. 不是 NSObject 協(xié)議! )。如果我們和 C 這樣的語言對比。在 C 里,你從 main() 方法開始寫然后就是從上到下的寫邏輯了并按你寫代碼的順序執(zhí)行程序。一個 C 的結(jié)構(gòu)體不能轉(zhuǎn)發(fā)函數(shù)執(zhí)行請求到其他的目標上(other targets)。很可能你的程序是這樣的:
所以我們現(xiàn)在總結(jié)一下運行時的主要用途。
1.//獲取變量列表數(shù)組
NSArray *varlist=[BGSRuntimeKit fetchIvarList:[TestClass class]];
NSLog(@"varlist:%@",varlist);
此方法對于抽象類,封裝特別有用,一個抽象類你不知道,這個類實例到底有多少屬性。那不妨用我封裝BGSRuntimekit獲取變量列表的方法,獲取此數(shù)組?,F(xiàn)在看一下此方法的具體封裝源碼
/**
動態(tài)獲取類的變量列表
@param class 要獲取列表的類
@return 所有變量的列表數(shù)組
*/
+(NSArray *)fetchIvarList:(Class)class {
unsigned int outCount =0;
NSArray *varListArray;
NSMutableArray *mListArray=[NSMutableArray array];
Ivar *ivarList=class_copyIvarList(class, &outCount);
for (int i=0; i<outCount;i++){
const char *ivarName=ivar_getName(ivarList[i]);
const char *ivarType=ivar_getTypeEncoding(ivarList[i]);
NSMutableDictionary *ivarListDic=[[NSMutableDictionary alloc]initWithCapacity:outCount];
[ivarListDic setObject:[NSString stringWithUTF8String:ivarName] forKey:@"name"];
[ivarListDic setObject:[NSString stringWithUTF8String:ivarType] forKey:@"type"];
[mListArray addObject:ivarListDic];
}
varListArray=[mListArray copy];
return varListArray;
}

2.第二個用途是,獲取目標類的 類名,此方法是最簡單的,
**
返回類名
@param cls 要返回類名的class
@return 返回類名的字符串
*/
+ (NSString *)fetchClassName:(Class)cls? {
const char *className=class_getName(cls);
NSString *clsName=[NSString stringWithUTF8String:className];
return clsName;
}
3.獲取目標類的屬性列表?
/**
獲取某個類的屬性列表
@param cls 目標類
@return 此類的屬性列表數(shù)組
*
+(NSArray *)fetchPropertyList:(Class)cls {
NSArray *propertyListArray;
unsigned int outcount=0;
objc_property_t *propertyList=class_copyPropertyList(cls, &outcount);
NSMutableArray *mutableList=[NSMutableArray arrayWithCapacity:outcount];
for (int i=0; i<outcount;i++){
const char *propertyName=property_getName(propertyList[i]);
[mutableList addObject:[NSString stringWithUTF8String:propertyName]];
}
propertyListArray=[mutableList copy];
free(propertyList);
return propertyListArray;
}
后臺打印屬性列表

4.獲取目標類的屬性類型列表,在封裝類的時候,往往不知道抽象類 到底有數(shù)據(jù)屬性,屬性類型是什么,對于不通類型,我們應該怎么應對。方法4和方法3,往往結(jié)合使用,既可以查看屬性名字,又可以查找屬性類型。
/**
獲取目標類的屬性類型列表
@param cls 目標類
@return 屬性類型列表的數(shù)組
*/
+(NSArray *)fetchPropertyTypeList:(Class)cls{
unsigned int outcount=0;
objc_property_t *propertyList=class_copyPropertyList(cls, &outcount);
NSMutableArray *marray=[NSMutableArray arrayWithCapacity:outcount];
for (int i=0; i<outCount;i++){
const char *propertyAttributes=property_getAttributes(propertyList[i]);
NSString *attributes=[NSString stringWithUTF8String:propertyAttributes];
NSLog(@"attributes:%@",attributes);
NSMutableString *mattributes=[NSMutableString stringWithString:attributes];
NSArray *componentArray=[mattributes componentsSeparatedByString:@","];
NSString *firstTypeWord=componentArray[0];
NSString *type=@"";
NSLog(@"firstTypeWord:%@",firstTypeWord);
if ([firstTypeWord containsString:@"@"]) {
NSMutableString *mtypeStr=[NSMutableString stringWithString:firstTypeWord];
NSArray *compsArray=[mtypeStr componentsSeparatedByString:@"\""];
type=compsArray[1];
NSLog(@"type:%@",type);
}else{
NSString? *typeShort=[firstTypeWord substringFromIndex:1];
//下面是對屬性類型的簡稱恢復成全城,方便辨別
if ([typeShort isEqualToString:@"i"]) {
type=@"int";
}
else if ([typeShort isEqualToString:@"B"]){
type=@"BOOL";
}
else if ([typeShort isEqualToString:@"f"]){
type=@"float";
}
else if ([typeShort isEqualToString:@"d"]){
type=@"double";
}
else if ([typeShort isEqualToString:@"q"]){
type=@"NSInteger";
}
else {
type=@"not know type";
}
}
NSLog(@"type:%@",type);
[marray addObject:type];
}
//記住此處一定釋放調(diào)屬性列表
free(propertyList);
return [marray copy];
}
后臺打印屬性類型列表

5.獲取目標類的方法列表,
/**
獲取目標類的方法列表
@param cls 目標類
@return 方法列表的數(shù)組
*/
+ (NSArray *)fetchMethodList:(Class)cls {
unsigned int outcount=0;
Method *mothodList=class_copyMethodList(cls, &outcount);
NSMutableArray *methodNameArry=[NSMutableArray arrayWithCapacity:outcount];
for (int i=0; i<outCount;i++){
Method m=mothodList[i];
SEL methodName=method_getName(m);
[methodNameArry addObject:NSStringFromSelector(methodName)];
}
free(mothodList);
return [methodNameArry copy];
}
后臺打印方法列表

6.獲取目標類的協(xié)議列表;有些類使用了哪些協(xié)議,利用此方法也能一目了然。
/**
獲取目標類的協(xié)議列表
@param cls 目標類
@return 目標類的協(xié)議列表數(shù)組
*/
+ (NSArray *)fetchProtocolList:(Class)cls {
unsigned int count=0;
__unsafe_unretained Protocol **protocolList=class_copyProtocolList(cls, &count);
NSMutableArray *protocolArray=[NSMutableArray arrayWithCapacity:count];
for (int i=0; i< count; i++) {
Protocol *protocol=protocolList[i];
const char *protocolName=protocol_getName(protocol);
[protocolArray addObject:[NSString stringWithUTF8String:protocolName]];
}
free(protocolList);
return [protocolArray copy];
}
7.為目標類添加動態(tài)方法,這個方法經(jīng)常用到,有些方法我不是寫程靜態(tài),我是動態(tài)加載。要想動態(tài)調(diào)用某個方法必須使用performSelector方法,這樣才能動態(tài)調(diào)用,動態(tài)生成方法。
/**
為目標類添加動態(tài)方法
@param class 目標類
@param methodSel 要添加的方法
@param impMethod 實際實現(xiàn)的方法
*/
+ (void)addMethod: (Class)class? method:(SEL)methodSel ImpMethod:(SEL)impMethod {
BOOL tag=NO;
Method method=class_getInstanceMethod(class, impMethod);
IMP methodImp=method_getImplementation(method);
const char *type=method_getTypeEncoding(method);
tag=class_addMethod(class, methodSel, methodImp, type);
if (tag) {
NSLog(@"addmethod successly");
}else{
NSLog(@"has the same name implemation method");
}
}
8.與此方法類似的,還有為目標類動態(tài)交換方法。
/**
為目標類動態(tài)交換方法
@param cls 目標類
@param fM 此類的第一個方法
@param sM 此類的第二個方法
*/
+ (void) methodSwap:(Class)cls firstMethod:(SEL)fM SecondMethod:(SEL)sM {
Method firstMethod= class_getInstanceMethod(cls, fM);
Method secondMethod=class_getInstanceMethod(cls, sM);
method_exchangeImplementations(firstMethod, secondMethod);
}