runtime ,小伙伴们在面试的会经常问到,runtime 里面的获取目标类的类名,方法名等方法一般是封装一般类共用方法的基础。所以我们有必要理清运行时到底是什么。它有什么用途,即方法用途。
程序员有时候会在什么东西应该在编译的时候加载进来以及什么东西该在运行的时候使用之间做出抉择,前者有时候被称为编译时期。
一段时间以来,技术类作者都拒绝使用"运行时刻"作为一种术语,他们坚持类似于"一个程序在运行"之类的说法,用以避免需要一个专门的术语。后来,这个术语逐渐地蔓延到通常的应用中。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),就是说它会尽可能的把编译和链接时要执行的逻辑延迟到运行时。这就给了你很大的灵活性,你可以按需要把消息重定向给合适的对象,你甚 至可以交换方法的实现,等等(译注:在 Objective-C 中调用一个对象的方法可以看成向一个对象发送消息, Method Swizzling 具体实现可以参看 jrswizzle )。这就需要使用 runtime,runtime 可以做对象自省查看他们正在做的和不能做的(don't respond to)并且合适的分发消息(译注:感兴趣的同学可以查看 NSObject 类的 – forwardingTargetForSelector: 和 – forwardInvocation: 方法。P.S. 不是 NSObject 协议! )。如果我们和 C 这样的语言对比。在 C 里,你从 main() 方法开始写然后就是从上到下的写逻辑了并按你写代码的顺序执行程序。一个 C 的结构体不能转发函数执行请求到其他的目标上(other targets)。很可能你的程序是这样的:
所以我们现在总结一下运行时的主要用途。
1.//获取变量列表数组
NSArray *varlist=[BGSRuntimeKit fetchIvarList:[TestClass class]];
NSLog(@"varlist:%@",varlist);
此方法对于抽象类,封装特别有用,一个抽象类你不知道,这个类实例到底有多少属性。那不妨用我封装BGSRuntimekit获取变量列表的方法,获取此数组。现在看一下此方法的具体封装源码
/**
动态获取类的变量列表
@param class 要获取列表的类
@return 所有变量的列表数组
*/
+(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 此类的属性列表数组
*
+(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.获取目标类的属性类型列表,在封装类的时候,往往不知道抽象类 到底有数据属性,属性类型是什么,对于不通类型,我们应该怎么应对。方法4和方法3,往往结合使用,既可以查看属性名字,又可以查找属性类型。
/**
获取目标类的属性类型列表
@param cls 目标类
@return 属性类型列表的数组
*/
+(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];
}
//记住此处一定释放调属性列表
free(propertyList);
return [marray copy];
}
后台打印属性类型列表
5.获取目标类的方法列表,
/**
获取目标类的方法列表
@param cls 目标类
@return 方法列表的数组
*/
+ (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.获取目标类的协议列表;有些类使用了哪些协议,利用此方法也能一目了然。
/**
获取目标类的协议列表
@param cls 目标类
@return 目标类的协议列表数组
*/
+ (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.为目标类添加动态方法,这个方法经常用到,有些方法我不是写程静态,我是动态加载。要想动态调用某个方法必须使用performSelector方法,这样才能动态调用,动态生成方法。
/**
为目标类添加动态方法
@param class 目标类
@param methodSel 要添加的方法
@param impMethod 实际实现的方法
*/
+ (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.与此方法类似的,还有为目标类动态交换方法。
/**
为目标类动态交换方法
@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);
}