您的位置:首页 > 移动开发 > Objective-C

Objective-C研究之Runtime分析(四)-Dynamic Method Resolution

2014-07-19 17:12 246 查看

Objective-C Runtime是Objective-C的基础内容,理解了Objective-C Runtime对于掌握Objective-C的很多技术原理非常有用。本站特别整理了Objective-C Runtime的内容,共六篇,本文是第四篇:前篇我们学习了
Runtime分析(三)-objc_msgSend
很多中文文档把”Dynamic Method Resolution”叫做”动态决议”
Objective-C 提供了一种名为动态方法决议的手段,使得我们可以在运行时动态地为一个 selector 提供实现。我们只要实现 +resolveInstanceMethod:或者 +resolveClassMethod: 方法,并在其中为指定的
selector 提供实现即可(通过调用运行时函数 class_addMethod 来添加)。这两个方法都是 NSObject 中的类方法,其原型为:

+ (BOOL)resolveClassMethod:(SEL)sel NS_AVAILABLE(10_5, 2_0);
+ (BOOL)resolveInstanceMethod:(SEL)sel NS_AVAILABLE(10_5, 2_0);

参数 sel 是需要被动态决议的 selector;返回值表示动态决议成功与否,这是在不涉及消息转发的情况下。如果在该函数内为指定的 selector 提供实现,无论返回 YES 还是 NO,编译运行都是正确的;但如果在该函数内并不真正为 selector
提供实现,无论返回 YES 还是 NO,运行都会 crash,道理很简单,selector 并没有对应的实现,而又没有实现消息转发。resolveInstanceMethod 是为对象方法进行决议,而 resolveClassMethod 是为类方法进行决议。

一,理解Dynamic Method Resolution
Objective-C 2.0 中增加了@dynamic指令,表示变量对应的属性访问器方法是动态实现的,属性的setter与getter方法由用户自己实现,不自动生成(区别于@synthesize,如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie
var = _va)。这个时候,需要在NSObject中继承而来的+(BOOL) resolveInstanceMethod:(SEL) sel 方法中指定动态实现的方法。
示例:

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
27
28
29
30
31
32
33
34
35
36
37

TestObject.h

#import <Foundation/Foundation.h>
@interface
TestObject :
NSObject

@property
(retain
, nonatomic)
NSString *tekubaUrl;
@end

TestObject.m

#import "TestObject.h"
@implementation
TestObject

@dynamic tekubaUrl;

/*这是个C语言的方法*/
void
dynamicMethod(id
self,SEL
_cmd,float
w)

{
printf("Dynamic:http://www.tekuba.net");

}

/*使用了@dynamic就不要有setTekubaUrl:方法了,不然就不会走resolveInstanceMethod:*/
//-(void)setTekubaUrl:(NSString *)tekubaUrl

//{
// printf("Set:%s",[tekubaUrl UTF8String]);

//}

//Returun YES if the method was found and added to the receiver, otherwise NO.
+(BOOL)resolveInstanceMethod:(SEL)sel

{
NSString *methodName
= NSStringFromSelector(sel);

if([methodName
isEqualToString:@"setTekubaUrl:"])

{
class_addMethod([self
class],
sel,
(IMP)dynamicMethod,"v@:f");

return
YES;
}

return
[super
resolveInstanceMethod:sel];

}
@end

本站的理解resolveInstanceMethod:返回YES或者NO对于“Dynamic Method Resolution”的结果没有影响,返回值只是表示是否将方法添加到该接受者。文档中没说返回YES和NO的其他区别,本站测试的结果是返回YES和NO对于方法的调用没有影响。

这里大致分为以下几步:
(1)使用@dynamic
这里我们对于接口中的tekubaUrl在实现类中使用了@dynamic指令,你需要指定一个方法(本类或者其他类的都可以)作为tekubaUrl的setter、getter方法的运行时实现。这里,我们指定了TestObject.m中定义的函数,注意这是C语言的函数,不是Objective-C的方法。dynamicMethod作为tekubaUrl的setter方法的运行时实现。被指定为动态实现的方法的dynamicMethod的参数:第一个、第二个参数必须是id、SEL,第三个参数要按照原方法(例如:setTekubaUrl:(NSString))的参数定义。
特别说明:实际上除了@dynamic 标注的属性之外,如果你调用了类型中不存在的方法,也会被resolveInstanceMethod 或者resolveClassMethod 截获,但由于你没有处理,所以一般也会报错。所以,本站就觉得,如果重写一个类的resolveInstanceMethod:方法,将不能识别的方法全部使用class_addMethod转交到一个用户提示错误的方法上,那程序就不会因为找不到方法而出错了。

(2)覆盖resolveInstanceMethod:
接着要覆盖NSObject 的类方法resolveInstanceMethod:,这个方法会把需要动态实现的方法setTekubaUrl:的SEL传递进来,我们判断一下是否是需要动态实现的选择器,如果是,就把处理权转交给dynamicMethod。转交的过程是使用到运行时函数
class_addMethod(Class,SEL,IMP,char[])(objc/runtime.h)
这个方法有四个参数,Class 表示你要为哪个类型增加方法,SEL 参数表示你要增加的方法的选择器,IMP 表示你要添加的方法的运行时具体实现的函数指针,第四个参数char[],是后面的《Objective-C Runtime分析(六)-Type Encodings》需要讲到的,这里就先不说。

二,文档翻译:
There are situations where you might want to provide an implementation of a method dynamically. For example, the Objective-C
declared properties feature (see “Declared Properties” in The Objective-C Programming Language) includes the @dynamic directive:
有些情况下你可能需要动态的提供一个方法的实现,比如Objective-C的@dynamic指示符,它告诉编译器与属性对应的方法是动态提供的。你需要在NSObject 中继承而来的+(BOOL) resolveInstanceMethod:(SEL)
sel 等方法中指定动态实现的方法或者函数。
如下:
@dynamic propertyName;
which tells the compiler that the methods associated with the property will be provided dynamically.
You can implement the methods resolveInstanceMethod: and resolveClassMethod: to dynamically provide an implementation for a
given selector for an instance and class method respectively.
我们可以利用resolveInstanceMethod:和resolveClassMethod:分别为对象方法和类方法提供动态实现。
An Objective-C method is simply a C function that take at least two arguments—self and _cmd. You can add a function to a class
as a method using the function class_addMethod. Therefore, given the following function:
一个Objective-C方法本质上是一个拥有至少两个参数(self和_cmd)的C函数,我们可以利用class_addMethod向一个类添加一个方法。比如对于下面的函数:
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ….
}
我们可以利用resolveInstanceMethod:将它添加成一个方法(比如叫resolveThisMethodDynamically):
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, “v@:”);
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
上面的Demo提供这样一个C函数 dynamicMethodIMP ,让它来充当对象方法 resolveThisMethodDynamically 这个 selector 的动态实现。因为 resolveThisMethodDynamically 是被对象所调用,所以它被认为是一个对象方法,因而应该在
resolveInstanceMethod 方法中为其提供实现。通过调用
class_addMethod([self class], name, (IMP)dynamicMethodIMP , “v@:”);
就能在运行期动态地为 resolveThisMethodDynamically 这个 selector 添加实现:dynamicMethodIMP。class_addMethod 是运行时函数,所以需要导入头文件:objc/runtime.h。
Forwarding methods (as described in “Message Forwarding”) and dynamic method resolution are, largely, orthogonal. A class has
the opportunity to dynamically resolve a method before the forwarding mechanism kicks in. If respondsToSelector: or instancesRespondToSelector: is invoked, the dynamic method resolver is given the opportunity to provide an IMP for the selector first. If you
implement resolveInstanceMethod: but want particular selectors to actually be forwarded via the forwarding mechanism, you return NO for those selectors.
动态决议和发送消息并不冲突,在消息机制起作用之前,一个类是有机会动态决议一个方法的。当respondsToSelector:或者instancesRespondToSelector:被激活时,dynamic method resolver会优先有个机会为这个selector提供一份实现。如果实现了resolveInstanceMethod:,对于不想动态决议而想让其遵循消息转发机制的selectors,返回NO即可。

Dynamic Loading
An Objective-C program can load and link new classes and categories while it’s running. The new code is incorporated into the
program and treated identically to classes and categories loaded at the start.
Dynamic loading can be used to do a lot of different things. For example, the various modules in the System Preferences application
are dynamically loaded.
In the Cocoa environment, dynamic loading is commonly used to allow applications to be customized. Others can write modules
that your program loads at runtime—much as Interface Builder loads custom palettes and the OS X System Preferences application loads custom preference modules. The loadable modules extend what your application can do. They contribute to it in ways that you
permit but could not have anticipated or defined yourself. You provide the framework, but others provide the code.
Although there is a runtime function that performs dynamic loading of Objective-C modules in Mach-O files (objc_loadModules,
defined in objc/objc-load.h), Cocoa’s NSBundle class provides a significantly more convenient interface for dynamic loading—one that’s object-oriented and integrated with related services. See the NSBundle class specification in the Foundation framework reference
for information on the NSBundle class and its use. See OS X ABI Mach-O File Format Reference for information on Mach-O files.
Objective-C程序可以在运行时链接新的类和category。动态加载可以用来做很多不同的事情,比如System Preferences里头各种模块就是动态加载的。尽管有运行时函数可以动态加载Objective-C模块(objc/objc-load.h中的objc_loadModules),但Cocoa的NSBundle类提供了更方便的动态加载接口。
(有待考察)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: