转自:http://www.cnblogs.com/xwang/p/3542515.html
转自:http://blog.csdn.net/kesalin/article/details/7178871
在Objective-C中,消息是通过objc_msgSend()
这个runtime方法及相近的方法来实现的。这个方法需要一个target,selector,还有一些参数。理论上来说,编译器只是把消息分发变成objc_msgSend
来执行。比如下面这两行代码是等价的。
1 [array insertObject:foo atIndex:5];
2 objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);
class的方法列表其实是一个字典,key为selectors,IMPs为value。一个IMP是指向方法在内存中的实现。很重要的一点是,selector和IMP之间的关系是在运行时才决定的,而不是编译时。这样我们就能玩出些花样。
IMP通常是指向方法的指针,第一个参数是self,类型为id,第二个参数是_cmd,类型为SEL,余下的是方法的参数。这也是self
和_cmd
被定义的地方。下面演示了Method和IMP
1 - (id)doSomethingWithInt:(int)aInt{}
2 id doSomethingWithInt(id self, SEL _cmd, int aInt){}
objc中存在一些用于修改和自省的方法,这些方法差不多都是以特定的前缀开头,如
class_addIvar, class_addMethod,class_addProperty和class_addProtocol
允许重建classes,
1
|
class_copyIvarList,class_copyMethodList, class_copyProtocolList和class_copyPropertyList
|
能拿到一个class的所有内容,而
1
|
class_getClassMethod, class_getClassVariable, class_getInstanceMethod,class_getInstanceVariable, class_getMethodImplementation和class_getProperty
|
返回单个内容。也有些用于自省的方法,如
1
|
class_conformsToProtocol, class_respondsToSelector,class_getSuperclass。
|
最后,你可以使用
class_createInstance
来创建一个object。
比较基础的一个动态特性是通过String来生成Classes和Selectors。Cocoa提供了NSClassFromString
和NSSelectorFromString
方法,使用起来很简单
1 Class stringclass = NSClassFromString(@"NSString");
2 NSString *myString = [stringclass stringWithString:@"Hello World"];
为什么要这么做呢,直接使用class不是更方便,通常情况下,但有些场景有会很能有用,首先可以得知某个class是否存在,因为不存在的话NSClassFromString会返回nil,用于检查。另一个常见的场景是根据不同的输入返回不同的class跟method。在解析数据的时候,下面是一个例子
- (void)parseObject:(id)object {
for (id data in object) {
if ([[data type] isEqualToString:@"String"]) {
[self parseString:[data value]];
} else if ([[data type] isEqualToString:@"Number"]) {
[self parseNumber:[data value]];
} else if ([[data type] isEqualToString:@"Array"]) {
[self parseArray:[data value]];
}
}
}
- (void)parseObjectDynamic:(id)object {
for (id data in object) {
[self performSelector:NSSelectorFromString([NSString stringWithFormat:@"parse%@:", [data type]]) withObject:[data value]];
}
}
- (void)parseString:(NSString *)aString {}
- (void)parseNumber:(NSString *)aNumber {}
- (void)parseArray:(NSString *)aArray {}
可以看到用了后者的话可以把代码行数降下来,将来如果有新的类型,只需要增加实现方法即可,而不用去添加新的else if.
在objc中,方法由两部分组成,selector相当于一个方法的id,IMP是方法的实现,这样分开的一个便利之处就是selector和IMP之间的对应关系可以被改变。这就是Method Swizzling的存在处,交换两个方法的实现,下面是代码实现:
1 void MethodSwizzle(Class aClass, SEL orig_sel, SEL alt_sel){
2 Method orig_method = nil, alt_method = nil;
3 // First, look for the methods
4 orig_method = class_getInstanceMethod(aClass, orig_sel);
5 alt_method = class_getInstanceMethod(aClass, alt_sel);
6 // If both are found, swizzle them
7 if ((orig_method != nil) && (alt_method != nil)){
8 char *temp1;
9 IMP temp2;
10 temp1 = orig_method->method_types;
11 orig_method->method_types = alt_method->method_types;
12 alt_method->method_types = temp1;
13 temp2 = orig_method->method_imp;
14 orig_method->method_imp = alt_method->method_imp;
15 alt_method->method_imp = temp2;
16 }
17 }
当然,上面的第八行开始到十五行之间的代码可以用如下进行替换。
method_exchangeImplementations(orig_method,alt_method)
上面我们谈到了方法交换,但是当你发送了一个object无法处理的消息时会发生什么呢?这里首先会是动态方法处理
1 resolveInstanceMethod && resolveClassMethod
在这两个重写的地方运用class_addMethod,同时记得返回YES,下面是一个例子
1 + (BOOL)resolveInstanceMethod:(SEL)aSelector {
2 if (aSelector == @selector(myDynamicMethod)) {
3 class_addMethod(self, aSelector, (IMP)myDynamicIMP, "v@:");
4 return YES;
5 }
6 return [super resolveInstanceMethod:aSelector];
7 }
如果resolve method返回了NO,那么运行时就进入下一个步骤--消息转发。首先会调用-forwardingTargetForSelector:,如果只是把消息发送到另一个object,那么就用这个方法,但是如果你想修改消息,那么就要使用-forwardInvocation:,将消息打包成NSInvocation,调用invokeWithTarget:
整个文章下来,可以看到objc表面看起来跟c#,java等语言在方法调用上没什么区别,但最关键的是objc的运行时消息处理,我们可以在消息处理上添加更多的自由,其优势在于在不扩展语言本身的情况下做很多事,比如KVO,提供了优雅的API来与已有的代码进行无疑结合。
下面就结合运行时来谈谈KVO的内部真正实现。当你第一次观察某个object时,runtime会创建一个新的继承原先class的subclass。在这个新的class中,它重写了所有被观察的key,然后将object的isa
指针指向新创建的class(这个指针告诉Objective-C运行时某个object到底是哪种类型的object)。所以object神奇地变成了新的子类的实例。
下面转自: Cocoa]深入浅出Cocoa之 Method Swizzling
Foo.h
- //
- // Foo.h
- // MethodSwizzling
- //
- // Created by LuoZhaohui on 1/5/12.
- // Copyright (c) 2012 http://blog.csdn.net/kesalin/. All rights reserved.
- //
- #import <Foundation/Foundation.h>
- // Foo
- //
- @interface Foo : NSObject
- - (void) testMethod;
- - (void) baseMethod;
- - (void) recursionMethod;
- @end
- // Bar
- //
- @interface Bar : Foo
- - (void) testMethod;
- @end
- // Bar(BarCategory)
- //
- @interface Bar(BarCategory)
- - (void) altTestMethod;
- - (void) altBaseMethod;
- - (void) altRecursionMethod;
- @end
Foo.m
- //
- // Foo.m
- // MethodSwizzling
- //
- // Created by LuoZhaohui on 1/5/12.
- // Copyright (c) 2012 http://blog.csdn.net/kesalin/. All rights reserved.
- //
- #import "Foo.h"
- // Foo
- //
- @implementation Foo
- - (void) testMethod
- {
- NSLog(@" >> Foo testMethod");
- }
- - (void) baseMethod
- {
- NSLog(@" >> Foo baseMethod");
- }
- - (void) recursionMethod
- {
- NSLog(@" >> Foo recursionMethod");
- }
- @end
- // Bar
- //
- @implementation Bar
- - (void) testMethod
- {
- NSLog(@" >> Bar testMethod");
- }
- @end
- // Bar(BarCategory)
- //
- @implementation Bar(BarCategory)
- - (void) altTestMethod
- {
- NSLog(@" >> Bar(BarCategory) altTestMethod");
- }
- - (void) altBaseMethod
- {
- NSLog(@" >> Bar(BarCategory) altBaseMethod");
- }
- - (void) altRecursionMethod
- {
- NSLog(@" >> Bar(BarCategory) recursionMethod");
- [self altRecursionMethod];
- }
- @end
下面是具体的使用示例:
- int main (int argc, const char * argv[])
- {
- @autoreleasepool
- {
- Foo * foo = [[[Foo alloc] init] autorelease];
- Bar * bar = [[[Bar alloc] init] autorelease];
- NSLog(@"========= Method Swizzling test 1 =========");
- NSLog(@" Step 1");
- [foo testMethod];
- [bar testMethod];
- [bar altTestMethod];
- NSLog(@" Step 2");
- [Bar swizzleMethod:@selector(testMethod) withMethod:@selector(altTestMethod)];
- [foo testMethod];
- [bar testMethod];
- [bar altTestMethod];
- NSLog(@"========= Method Swizzling test 2 =========");
- NSLog(@" Step 1");
- [foo baseMethod];
- [bar baseMethod];
- [bar altBaseMethod];
- NSLog(@" Step 2");
- [Bar swizzleMethod:@selector(baseMethod) withMethod:@selector(altBaseMethod)];
- [foo baseMethod];
- [bar baseMethod];
- [bar altBaseMethod];
- NSLog(@"========= Method Swizzling test 3 =========");
- [Bar swizzleMethod:@selector(recursionMethod) withMethod:@selector(altRecursionMethod)];
- [bar recursionMethod];
- }
- return 0;
- }
输出结果为:注意,test 3 中调用了递归调用“自己”的方法,你能理解为什么没有出现死循环么?
========= Method Swizzling test 1 =========
Step 1
>> Foo testMethod
>> Bar testMethod
>> Bar(BarCategory) altTestMethod
Step 2
>> Foo testMethod
>> Bar(BarCategory) altTestMethod
>> Bar testMethod
========= Method Swizzling test 2 =========
Step 1
>> Foo baseMethod
>> Foo baseMethod
>> Bar(BarCategory) altBaseMethod
Step 2
>> Foo baseMethod
>> Bar(BarCategory) altBaseMethod
>> Foo baseMethod
========= Method Swizzling test 3 =========
>> Bar(BarCategory) recursionMethod
>> Foo recursionMethod
test3 解释:在函数体 {} 之间的部分是真正的 IMP,而在这之前的是 SEL。通常情况下,SEL 是与 IMP 匹配的,但在 swizzling 之后,情况就不同了。下图就是调用的时序图。
rentzsch 写了一个完善的开源类 jrswizzle 来处理 Method Swizzling,如果你在工程中使用到 Method Swizzling手法,应该优先使用这个类库,:)。
Refference:
MethodSwizzling:http://www.cocoadev.com/index.pl?ExtendingClasses
jrswizzle:https://github.com/rentzsch/jrswizzle