objc运行时,方法交换(交换方法)踩过的坑

说明:

    一个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



转载于:https://my.oschina.net/quntion/blog/614312

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值