You should never rely on the isa pointer to determine class membership. Instead, you should use the class method to determine the class of an object instance.
类似的,使用isa swizzling的技术的还有系统提供的Key-Value Coding(KVC)。 (谢谢大家指出错误,KVC并没有使用到isa swizzling)
Method Swizzling API说明
Objective-C提供了以下API来动态替换类方法或实例方法的实现:
class_replaceMethod替换类方法的定义method_exchangeImplementations交换2个方法的实现method_setImplementation设置1个方法的实现这3个方法有一些细微的差别,给大家介绍如下:
class_replaceMethod在苹果的文档(如下图所示)中能看到,它有两种不同的行为。当类中没有想替换的原方法时,该方法会调用class_addMethod来为该类增加一个新方法,也因为如此,class_replaceMethod在调用时需要传入types参数,而method_exchangeImplementations和method_setImplementation却不需要。
method_exchangeImplementations的内部实现其实是调用了2次method_setImplementation方法,从苹果的文档中能清晰地了解到(如下图所示)
从以上的区别我们可以总结出这3个API的使用场景:
class_replaceMethod, 当需要替换的方法可能有不存在的情况时,可以考虑使用该方法。method_exchangeImplementations,当需要交换2个方法的实现时使用。method_setImplementation最简单的用法,当仅仅需要为一个方法设置其实现方式时使用。使用示例
我们在开发猿题库客户端的笔记功能时,需要使用系统的
UIImagePickerController。但是,我们发现,在iOS6.0.2系统下,系统提供的UIImagePickerController在iPad横屏下有转屏的Bug,造成其方向错误。具体的Bug详情可以见这里。为了修复该Bug,我们需要替换
UIImagePickerController的如下2个方法1 2
- (BOOL)shouldAutorotate; - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation;我们先实现了一个名为
ImagePickerReplaceMethodsHolder的类,用于定义替换后的方法和实现。如下所示:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// ImagePickerReplaceMethodsHolder.h @interface ImagePickerReplaceMethodsHolder : NSObject - (BOOL)shouldAutorotate; - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation; @end // ImagePickerReplaceMethodsHolder.m @implementation ImagePickerReplaceMethodsHolder - (BOOL)shouldAutorotate { return NO; } - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation { return UIInterfaceOrientationPortrait; } @end然后,我们在调用处,判断当前的iOS版本,对于[iOS6.0, iOS6.1)之间的版本,我们将
UIImagePickerController的有问题的方法替换。具体代码如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [self hackForImagePicker]; }); } + (void)hackForImagePicker { // fix bug of image picker under iOS 6.0 // http://stackoverflow.com/questions/12522491/crash-on-presenting-uiimagepickercontroller-under-ios-6-0 if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0") && SYSTEM_VERSION_LESS_THAN(@"6.1")) { Method oldMethod1 = class_getInstanceMethod([UIImagePickerController class], @selector(shouldAutorotate)); Method newMethod1 = class_getInstanceMethod([ImagePickerReplaceMethodsHolder class], @selector(shouldAutorotate)); method_setImplementation(oldMethod1, method_getImplementation(newMethod1)); Method