说明:
一个oc类的+load方法是在app开始运行时首先会被调用执行的方法,也就是说,当app被点击,再被系统加载app程序进入内存后,首先会实例化所有类到代码或全局区(这里不做深究),而加载所有类的设置时,就会调用类的load方法,如果要给一个类做方法交换,则一般情况放在load方法中来操作。方法交换一旦完成,则程序运行中全局生效。
注意:
一个类B可能有继承来的super类A,还有可能有自己的分类C,如果分类中也实现了load方法,它们的调用顺序是怎么样的呢?系统首先会调用super的load方法,然后再调用类B自身的load方法,再次才会调用类B的分类C的load方法,也即是说真个继承链包括分类扩展中的load方法都会被执行到,只是执行顺序需要关注一下。load方法不同于其他覆盖方法在分类中的体现,如果类B本身中的其他方法在分类C中被重写,则会优先执行分类C中的。但是load不同,都会被执行到,因为这是类加载设置的方法。
好了,不罗嗦,开始撸代码~
我把方法交换的实现放到控制器
SHTableViewController 的load方法中,(在哪个load中实现方法交换都一样,因为在所有类的load方法都执行完,才会去创建对应的对象,所以可以在类a的load中给类b实施方法交换,明白?!)
代码如下,代码中详解了坑在哪!
//
// SHTableViewController.m
// 工具类集成
//
// Created by 邵瑞波 on 16/2/7.
// Copyright © 2016年 邵瑞波. All rights reserved.
//
#import "SHTableViewController.h"
#import <objc/runtime.h>
@interface SHTableViewController ()
@end
@implementation SHTableViewController
/*!
load的加载顺序是,先加载super的load方法,然后加载own的load,
然后加载分类中的load方法,所以这里不需要调用super的load方法。
*/
+ (void)load {
NSLog(@"load-[%s]", __FUNCTION__);
// // 交换方法
Method orgMet = class_getInstanceMethod([UIControl class], @selector(sendActionsForControlEvents:));
// IMP orgSel = method_getImplementation(orgMet);
// 因为 srb_sendActionsForControlEvents: 方法本身是 当前控制器的方法,所以这里的第一个参数是 [self class], 坑就在这里。
Method newMet = class_getInstanceMethod([self class], @selector(srb_sendActionsForControlEvents:));
// IMP newSel = method_getImplementation(orgMet);
// 给 UIControl 添加方法
class_addMethod([UIControl class], @selector(srb_sendActionsForControlEvents:), method_getImplementation(newMet), method_getTypeEncoding(newMet));
NSLog(@"orgMet=[%p], newMet=[%p]", orgMet, newMet);
// 这里的 UIControl 类上 新增的 方法的 Method 必须重新获取地址,否则,在下面交换时 有问题,不会报错,但是 并没有真正的 将两个方法交换,
// 导致执行时,出现 死循环调用。 切记切记!!!!!!!!!!!!!!!!!!!!!!!!!!
newMet = class_getInstanceMethod([UIControl class], @selector(srb_sendActionsForControlEvents:));
NSLog(@"orgMet1=[%p], newMet1=[%p]", orgMet, newMet);
method_exchangeImplementations(orgMet, newMet);
Method orgMet2 = class_getInstanceMethod([UIControl class], @selector(sendActionsForControlEvents:));
// IMP orgSel = method_getImplementation(orgMet);
Method newMet2 = class_getInstanceMethod([UIControl class], @selector(srb_sendActionsForControlEvents:));
NSLog(@"orgMet2=[%p], newMet2=[%p]", orgMet2, newMet2);
// 交换方法
// Method orgMet1 = class_getInstanceMethod([UIControl class], @selector(beginTrackingWithTouch:withEvent:));
// // IMP orgSel = method_getImplementation(orgMet);
//
// Method newMet1 = class_getInstanceMethod([UIControl class], @selector(srb_beginTrackingWithTouch:withEvent:));
// // IMP newSel = method_getImplementation(orgMet);
//
// NSLog(@"orgMet1=[%p], newMet1=[%p]", orgMet1, newMet1);
//
// method_exchangeImplementations(orgMet1, newMet1);
//
// Method orgMet2 = class_getInstanceMethod([UIControl class], @selector(beginTrackingWithTouch:withEvent:));
// // IMP orgSel = method_getImplementation(orgMet);
//
// Method newMet2 = class_getInstanceMethod([UIControl class], @selector(srb_beginTrackingWithTouch:withEvent:));
// // IMP newSel = method_getImplementation(orgMet);
//
// NSLog(@"orgMet2=[%p], newMet2=[%p]", orgMet2, newMet2);
}
- (void)srb_sendActionsForControlEvents:(UIControlEvents)controlEvents {
NSLog(@"xxx=[%zd]", controlEvents);
[self srb_sendActionsForControlEvents:controlEvents];
NSLog(@"oooooo=[%zd]", controlEvents);
}
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(@"xxxxxsssssssssssssssss");
// return [self srb_beginTrackingWithTouch:touch withEvent:event];
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations.
// self.clearsSelectionOnViewWillAppear = NO;
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.tableView.contentInset = UIEdgeInsetsMake(60, 0, 0, 0);
self.tableView.editing = YES;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"第 %zd 条", indexPath.row];
// cell.backgroundColor = [UIColor redColor];
return cell;
}
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the specified item to be editable.
return YES;
}
*/
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
} else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
// Return NO if you do not want the item to be re-orderable.
return YES;
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end