在part2中,我们知道由三种途径可以和运行时打交道,使用最灵活的要数最后一种了,通过#import<objc/runtime.h>,我们就可以使用强大的运行时提供的全部功能了。不过,运行时是一把双刃剑,它能影响到你的所有代码的运行,所以除非很必要,一般最好还是尽少使用吧。

method swizzling

这次主要说一下method swizzling的用法,它通过添加/修改/删除SEL -> IMP的映射关系,来让运行时去执行指定的方法;这种方法在某些场景下非常有效,当我们有需要在类目中重载一个类的方法的需求时就可以使用method swizzling,因为在类目中重载方法会导致原有的方法永远没有机会去执行了,而且原有的方法的具体实现我们是无法知道的,这就可能会导致不可预测的危险,所以最好的是使用methor swizzling

下面通过一个我在实际中用到的一个例子还说明一下具体的使用方法。

在iOS7中,UINavigationController新增加了一个支持手势返回的API:

1
@property(nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0);

实际上它是UIScreenEdgePanGestureRecognizer类型的,但只支持从左边侧开始的手势,如果我们想要实现从任意位置开始的返回,最简单的方法就是使用 method Swizzling 来实现,这个灵感来自于这里这里.

  • 首先先实现一个用于method swizzling的C函数:
1
2
3
4
5
6
7
8
void MethodSwizzle(Class c, SEL origSEL, SEL overrideSEL){

Method origMethod = class_getInstanceMethod(c, origSEL);
Method overrideMethod = class_getInstanceMethod(c, overrideSEL);

method_exchangeImplementations(origMethod, overrideMethod);

}
  • 然后实现一个UIScreenEdgePanGestureRecognizer的类目,我们只需要将UIScreenEdgePanGestureRecognizer替换成UIPanGestureRecognizer即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
+ (void)load{

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
MethodSwizzle([self class], @selector(initWithTarget:action:), @selector(initPanWithTarget:action:));
});

}

- (id)initPanWithTarget:(id)target action:(SEL)action{

if (enableGesturePop) {
enableGesturePop = NO;
self = (UIScreenEdgePanGestureRecognizer *)[[UIPanGestureRecognizer alloc] initWithTarget:target action:action];
} else {
self = [self initPanWithTarget:target action:action];
}
return self;
}

需要注意的是我们在程序中似乎是使用了递归调用,其实不然,因为我们之前在+load()中已经交换了这两个方法的SEL -> IMP映射。

我们可以通过enableGesturePop来决定是使用自定义的返回功能还是使用系统提供的返回功能。

最后的效果如下图所示:

image

关于运行时的其他用法,比如通过category给类添加属性和变量。因为类的结构体确定好之后,大小就不能改变了,这种方法其实是通过Associated Objects来进行添加的;其他的应有还有IMP缓存、Dynamic NSCoding,想深入学习的话可以参考一下参考资料中的内容。

参考资料

  1. Objective-C Runtime Reference

  2. 让Category支持添加属性与成员变量

  3. Associated Objects

  4. Associated Objects-翻译版

  5. Method Swizzling

  6. Objective-C对象模型及应用

  7. 深入Objective-C的动态特性

  8. Dynatic NSCoding

  9. Delegate的IMP缓存

  10. Method Replacement for Fun and Profit

Updata

Github上发现了一个从任意位置滑动手势返回的完整实现:SloppySwipe