一般来说,系统提供的方法已经足够开发了,但是有的时候有些需求用普通方法不好做。
如:在所有的viewcontroll 的viewwillappear:方法之前打个log
你可能会这么做:
1. 建一个uiviewcontroll 父类,重写viewwillappear方法,调用super viewwillappear 方法之前加上log
2. 所有新建的uiviewcontroller 继承第一步生成的
确实你是完成这样的功能,可是你做了那么多的修改,基本每个uiviewcontroller都去修改了父类,这种方法太过于笨重了
本文提供了简单地方法即可实现
我的理解中,object-c 的类调用方法是根据三个元素来定义的。
1. 方法,代表类定义中一个方法类型(typedef struct objc_method *Method)
2. SEL 选择器(typedef struct objc_selector *SEL),一个方法在运行时的名字,常见的有 [self performSelector:@selector(somemethod:) withObject:nil afterDelay:0.5]; @selector(somemethod:)作为方法的入口
3. 方法的实现入口(typedef id (*IMP)(id, SEL, …))
这三个元素确定了具体调用哪一个函数
直接看代码
[objc] view plaincopyprint?
- #import "UIViewController+Tracking.h"
- #import <objc/runtime.h>
- @implementation UIViewController (Tracking)
- + (void)load {
- NSString *className = NSStringFromClass(self.class);
- NSLog(@"classname %@", className);
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- Class class = [self class];
- // When swizzling a class method, use the following:
- // Class class = object_getClass((id)self);
- SEL originalSelector = @selector(viewWillAppear:);
- SEL swizzledSelector = @selector(xxx_viewWillAppear:);
- Method originalMethod = class_getInstanceMethod(class, originalSelector);
- Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
- BOOL didAddMethod =
- class_addMethod(class,
- originalSelector,
- method_getImplementation(swizzledMethod),
- method_getTypeEncoding(swizzledMethod));
- if (didAddMethod) {
- class_replaceMethod(class,
- swizzledSelector,
- method_getImplementation(originalMethod),
- method_getTypeEncoding(originalMethod));
- } else {
- method_exchangeImplementations(originalMethod, swizzledMethod);
- }
- });
- }
我们category重写了NSObject的 load 方法oc提供了objc/runtime.h类让我们获取这些东西,同时还提供了对类方法操作的函数
我们想的是,直接用一个方法替换掉系统的方法,然后把一些自定义的动作加到方法中
我们只想运行一次就够了,所以使用了 dispatch_once(&onceToken, ^{ …… }
接下来给类添加了新方法
把新方法和系统方法替换
[objc] view plaincopyprint?
- #pragma mark - Method Swizzling
- - (void)xxx_viewWillAppear:(BOOL)animated {
- NSLog(@"viewWillAppear: %@", self);
- [self xxx_viewWillAppear:animated];
- }
但是新方法实现的时候,调用的是 [self xxx_viewwillAppear:animated]; 可能你会疑惑
这是因为我们在上面已经用xxx_viewwillAppear 和 viewwillAppear 互换了。所以实际上执行的是系统的viewwillAppear
这个时候可能你又有疑问了,为什么实现是- (void)xxx_viewWillAppear:(BOOL)animated{} 这样的
这是因为 SEL swizzledSelector = @selector(xxx_viewWillAppear:); 拿的就是我们新写的方法。
可以结合这篇博客看,配图很容易懂
http://blog.csdn.net/yiyaaixuexi/article/details/9374411
以及这篇对SEL讲的比较清楚
http://blog.csdn.net/fengsh998/article/details/8612969
代码下载地址
https://github.com/holysin/Method_swizzle