from:http://www.cnblogs.com/gugupluto/p/3159733.html#aaaaaaaaaaaaaaaaaaa
前言:
Objective C的runtime技术功能非常强大,能够在运行时获取并修改类的各种信息,包括获取方法列表、属性列表、变量列表,修改方法、属性,增加方法,属性等等,本文对相关的几个要点做了一个小结。
目录:
(1)使用class_replaceMethod/class_addMethod函数在运行时对函数进行动态替换或增加新函数
(2)重载forwardingTargetForSelector,将无法处理的selector转发给其他对象
(3)重载resolveInstanceMethod,从而在无法处理某个selector时,动态添加一个selector
(4)使用class_copyPropertyList及property_getName获取类的属性列表及每个属性的名称
(5) 使用class_copyMethodList获取类的所有方法列表
(1)在运行时对函数进行动态替换 : class_replaceMethod
使用该函数可以在运行时动态替换某个类的函数实现,这样做有什么用呢?最起码,可以实现类似windows上hook效果,即截获系统类的某个实例函数,然后塞一些自己的东西进去,比如打个log什么的。
示例代码:
IMP orginIMP; NSString * MyUppercaseString(id SELF, SEL _cmd) { NSLog(@"begin uppercaseString"); NSString *str = orginIMP (SELF, _cmd);(3) NSLog(@"end uppercaseString"); return str; } -(void)testReplaceMethod { Class strcls = [NSString class]; SEL oriUppercaseString = @selector(uppercaseString); orginIMP = [NSString instanceMethodForSelector:oriUppercaseString]; (1) IMP imp2 = class_replaceMethod(strcls,oriUppercaseString,(IMP)MyUppercaseString,NULL);(2) NSString *s = "hello world"; NSLog(@"%@",[s uppercaseString]]; }
执行结果为:
begin uppercaseString
end uppercaseString
HELLO WORLD
这段代码的作用就是
(1)得到uppercaseString这个函数的函数指针存到变量orginIMP中
(2)将NSString类中的uppercaseString函数的实现替换为自己定义的MyUppercaseString
(3)在MyUppercaseString中,先执行了自己的log代码,然后再调用之前保存的uppercaseString的系统实现,这样就在系统函数执行之前加入自己的东西,后面每次对NSString调用uppercaseString的时候,都会打印出log来
与class_replaceMethod相仿,class_addMethod可以在运行时为类增加一个函数。
(2)当某个对象不能接受某个selector时,将对该selector的调用转发给另一个对象:- (id)forwardingTargetForSelector:(SEL)aSelector
forwardingTargetForSelector是NSObject的函数,用户可以在派生类中对其重载,从而将无法处理的selector转发给另一个对象。还是以上面的uppercaseString为例,如果用户自己定义的CA类的对象a,没有uppercaseString这样一个实例函数,那么在不调用respondSelector的情况下,直接执行[a performSelector:@selector"uppercaseString"],那么执行时一定会crash,此时,如果CA实现了forwardingTargetForSelector函数,并返回一个NSString对象,那么就相对于对该NSString对象执行了uppercaseString函数,此时就不会crash了。当然实现这个函数的目的并不仅仅是为了程序不crash那么简单,在实现装饰者模式时,也可以使用该函数进行消息转发。
示例代码:
1 @interface CA : NSObject 3 -(void)f; 4 5 @end 6 7 @implementation CA 8 9 - (id)forwardingTargetForSelector:(SEL)aSelector 11 { 13 if (aSelector == @selector(uppercaseString)) 15 { 17 return@"hello world"; 19 } 21 }
测试代码:
CA *a = [CA new]; NSString * s = [a performSelector:@selector(uppercaseString)]; NSLog(@"%@",s);
测试代码的输出为:HELLO WORLD
ps:这里有个问题,CA类的对象不能接收@selector(uppercaseString),那么如果我在forwardingTargetForSelector函数中用class_addMethod给CA类增加一个uppercaseString函数,然后返回self,可行吗?经过试验,这样会crash,此时CA类其实已经有了uppercaseString函数,但是不知道为什么不能调用,如果此时new一个CA类的对象,并返回,是可以成功的。
(3)当某个对象不能接受某个selector时,向对象所属的类动态添加所需的selector:
+ (BOOL) resolveInstanceMethod:(SEL)aSEL
这个函数与forwardingTargetForSelector类似,都会在对象不能接受某个selector时触发,执行起来略有差别。前者的目的主要在于给客户一个机会来向该对象添加所需的selector,后者的目的在于允许用户将selector转发给另一个对象。另外触发时机也不完全一样,该函数是个类函数,在程序刚启动,界面尚未显示出时,就会被调用。
在类不能处理某个selector的情况下,如果类重载了该函数,并使用class_addMethod添加了相应的selector,并返回YES,那么后面forwardingTargetForSelector就不会被调用,如果在该函数中没有添加相应的selector,那么不管返回什么,后面都会继续调用forwardingTargetForSelector,如果在forwardingTargetForSelector并未返回能接受该selector的对象,那么resolveInstanceMethod会再次被触发,这一次,如果仍然不添加selector,程序就会报异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
代码示例一: 1 @implementation CA
3 void dynamicMethodIMP( id self , SEL _cmd)
5 {
7 printf( "SEL %s did not exist\n" ,sel_getName(_cmd));
9 }
10 11 + ( BOOL ) resolveInstanceMethod:( SEL )aSEL
13 { 15 if (aSEL == @selector (t))
17 { 19 class_addMethod([selfclass], aSEL, (IMP) dynamicMethodIMP, "v@:" );
21 return YES ;
23 } 25 return [superresolveInstanceMethod:aSEL];
27 } 28 29 @end
测试代码: CA * ca = [CA new ]
[ca performSelector: @selector (t)];
|
执行结果
SEL t did not exist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
示例代码二: @implementation CA
void dynamicMethodIMP( id self , SEL _cmd)
{ printf( "SEL %s did not exist\n" ,sel_getName(_cmd));
} + ( BOOL ) resolveInstanceMethod:( SEL )aSEL
{ return YES ;
} - ( id )forwardingTargetForSelector:( SEL )aSelector
{ if (aSelector == @selector (uppercaseString))
{
return @ "hello world" ;
}
} 测试代码 : a = [[CA alloc]init];
NSLog (@ "%@" ,[a performSelector: @selector (uppercaseString)];
|
该测试代码的输出为:HELLO WORLD
对于该测试代码,由于a没有uppercaseString函数,因此会触发resolveInstanceMethod,但是由于该函数并没有添加selector,因此运行时发现找不到该函数,会触发
forwardingTargetForSelector函数,在forwardingTargetForSelector函数中,返回了一个NSString "hello world",因此会由该string来执行uppercaseString函数,最终返回大写的hello world。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
示例代码三: @implementation CA
+ ( BOOL ) resolveInstanceMethod:( SEL )aSEL
{ return YES ;
} - ( id )forwardingTargetForSelector:( SEL )aSelector
{ return nil ;
}
测试代码: 1 a = [[CA alloc]init]; 2 NSLog (@ "%@" ,[a performSelector: @selector (uppercaseString)];
|
这段代码的执行顺序为:
(1):首先在程序刚执行,AppDelegate都还没有出来时,resolveInstanceMethod就被触发,
(2)等测试代码执行时,forwardingTargetForSelector被调用
(3)由于forwardingTargetForSelector返回了nil,因此运行时还是找不到uppercaseString selector,这时又会触发resolveInstanceMethod,由于还是没有加入selector,于是会crash。
(4) 使用class_copyPropertyList及property_getName获取类的属性列表及每个属性的名称
u_int count; objc_property_t* properties= class_copyPropertyList([UIView class ], &count);
for ( int i = 0; i < count ; i++)
{ const char * propertyName = property_getName(properties[i]);
NSString *strName = [ NSString stringWithCString:propertyName encoding: NSUTF8StringEncoding ];
NSLog (@ "%@" ,strName);
} |
以上代码获取了UIView的所有属性并打印属性名称, 输出结果为:
skipsSubviewEnumeration viewTraversalMark viewDelegate monitorsSubtree backgroundColorSystemColorName gesturesEnabled deliversTouchesForGesturesToSuperview userInteractionEnabled tag layer _boundsWidthVariable _boundsHeightVariable _minXVariable _minYVariable _internalConstraints _dependentConstraints _constraintsExceptingSubviewAutoresizingConstraints _shouldArchiveUIAppearanceTags |
(5) 使用class_copyMethodList获取类的所有方法列表
获取到的数据是一个Method数组,Method数据结构中包含了函数的名称、参数、返回值等信息,以下代码以获取名称为例:
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);
} |
代码执行后将输出UIView所有函数的名称,具体结果略。
其他一些相关函数:
1.SEL method_getName(Method m) 由Method得到SEL 2.IMP method_getImplementation(Method m) 由Method得到IMP函数指针 3.const char *method_getTypeEncoding(Method m) 由Method得到类型编码信息 4.unsigned int method_getNumberOfArguments(Method m)获取参数个数 5.char *method_copyReturnType(Method m) 得到返回值类型名称 6.IMP method_setImplementation(Method m, IMP imp) 为该方法设置一个新的实现
总而言之,使用runtime技术能做些什么事情呢?
可以在运行时,在不继承也不category的情况下,为各种类(包括系统的类)做很多操作,具体包括:
- 增加
增加函数:class_addMethod 增加实例变量:class_addIvar 增加属性: @dynamic 标签,或者class_addMethod,因为属性其实就是由getter和setter函数组成
增加Protocol:class_addProtocol (说实话我真不知道动态增加一个protocol有什么用,-_-!!) |
- 获取
获取函数列表及每个函数的信息(函数指针、函数名等等):class_getClassMethod method_getName ... 获取属性列表及每个属性的信息:class_copyPropertyList property_getName 获取类本身的信息,如类名等:class_getName class_getInstanceSize 获取变量列表及变量信息:class_copyIvarList 获取变量的值 |
- 替换
将实例替换成另一个类:object_setClass 将函数替换成一个函数实现:class_replaceMethod 直接通过 char *格式的名称来修改变量的值,而不是通过变量
|
参考资料:(1)Objective-C Runtime Reference
(2)深入浅出Cocoa之消息
相关推荐
总结来说,Objective-C 2.0运行时系统编程指南为读者提供了一套关于Objective-C运行时系统操作的全面介绍,涵盖了从运行时系统的原理到具体的应用实践。无论是新手还是有经验的开发者,都能从中获得价值,特别是在...
Objective-C Runtime是Objective-C编程语言的核心部分之一,它提供了运行时系统的基本功能,包括消息传递机制、类和对象管理等。Runtime环境允许程序员在运行时动态地获取类的信息、方法列表以及实例变量等,这些...
《Objective-C 2.0 Runtime Programming Guide》是Apple公司为开发者提供的一份详尽的技术文档,专注于Objective-C 2.0运行时编程的深入解析。这份文档不仅对Objective-C语言的核心概念进行了阐述,还深入探讨了运行...
这意味着Objective-C语言不仅需要一个编译器,还需要一个运行时系统来执行编译的代码。Runtime系统是一种用于Objective-C语言的操作系统,它使OC语言工作起来。 Runtime的核心是在运行时动态操作类和消息分发给...
这意味着Objective-C不仅需要一个编译器,还需要一个运行时系统来执行编译后的代码。运行时系统在Objective-C中扮演着类似操作系统的重要角色,为Objective-C程序提供运行环境和支持。 本文将深入探讨如何通过...
本文档分为几个章节,分别介绍了Objective-C运行时系统的基本概念、关键功能以及如何利用这些功能来开发更灵活的应用程序。 #### 四、结论 尽管了解Objective-C运行时系统的细节不是编写Cocoa应用程序所必需的,但...
### Objective-C 2.0 运行时系统编程指南 #### 概述 Objective-C是一种面向对象的编程语言,它以其独特的动态性而闻名。这种动态性体现在它能够尽可能地将决策过程推迟到运行时执行,而不是在编译或链接阶段。因此...
Objective-C 运行时系统有两个版本:早期版本和现行版本(Objective-C 2.0)。现行版本的主要特点包括: - 实例变量的“健壮性”:在早期版本中,如果更改了类中实例变量的布局,则需要重新编译该类的所有子类。而...
内容概要:本文档介绍了 Objective-C 开发示例项目——RuntimeSummary,该项目主要涉及 Objective-C 的运行时特性,如消息机制、方法交换、动态加载方法、消息转发、动态关联属性、字典转模型以及对象归档和解档。...
Objective-C Runtime是Objective-C语言的核心,它负责在运行时管理类、对象、方法等。在objc4-723源码中,你可以看到以下关键组件: 1. 类与对象:Runtime提供了创建和管理类的机制。例如,`objc_class`结构体表示...
Objective-C 的运行时系统(Runtime System)是一种特殊的软件层,它位于编译好的程序和操作系统之间。运行时系统的主要职责包括: - **消息传递**:负责将方法调用转换成消息发送,并处理这些消息。 - **动态方法...
Objective-C运行时系统(Run time)是Objective-C语言的关键组成部分,它允许许多决策在编译时和链接时推迟到运行时进行。这意味着Objective-C不仅仅是依赖编译器,还需要一个运行时系统来执行编译后的代码。运行时...
iOS-Runtime-Headers, 从运行时自省导出的iOS Objective C 头 动态生成的iOS头以下是从运行时自省导出的iOS objective-c 头。头部是使用 RuntimeBrowser在iPhone上生成的。全文搜索你可以使用 github搜索插件搜索头...
Objective-C 2.0运行时系统编程指南。介绍从底层介绍iOS运行时机制。
Objective-C程序可以通过以下几种方式与运行时系统进行交互: 1. **通过Objective-C源代码**:在编写Objective-C源代码时,编译器会自动生成一些数据结构和函数,用于支持语言的动态特性。这些数据结构包含了类定义...
iOS运行时(Runtime)是Apple为其移动操作系统iOS和macOS提供的一种核心技术,它是一个底层的接口,使得开发者能够与Objective-C和Swift等编程语言的运行环境进行交互。在这个环境中,对象、类、方法以及消息传递等...