美文网首页runtime
iOS 高级开发(2)之 Runtime 方法交换

iOS 高级开发(2)之 Runtime 方法交换

作者: iOS刘耀宗 | 来源:发表于2021-08-15 13:24 被阅读0次

Demo下载
本篇文章我们来介绍 runtime 的方法交换. 这个功能也是 runtime 里面用的非常多的, 是一个必须掌握的技能.

我们将以一个示例来讲解
在实际开发需求中, 我们都会用到 tableView. 那么在有数据和无数据的情况下,显示不同. 无数据的情况下需要显示无数据信息, 无网络状态下要显示无网络.
实现的方法千千万万. 这里讲解如何用 runtime 来优雅的实现 tableView 无数据状态的切换
那么我们这里的构思是如果tableView 能够像 UITextField一样设置一个 placeholder. 没有数据的时候自动显示. 那我们只需要对 placeholder 进行一个设置就可以了.
思路:
tableView 的刷新 都要调用 [tableview reloadData]
那么我们对 reloadData 方法做一个交换. 在这里检测当前 tableView 页面是否有数据, 没有则显示空数据页面, 有则正常显示.

主要用到C的 API 如下,使用很简单, 主要以代码为主.

//获取方法
class_getInstanceMethod(Class _Nullable cls, SEL _Nonnull name)
//方法交换
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) 

主要代码如下:

//
//  UITableView+placeHolder.m
//  test2
//
//  Created by liuyaozong on 2021/8/15.
//

#import "UITableView+placeHolder.h"
#import <objc/runtime.h>
#import <objc/message.h>
@interface UITableView ()


@end
@implementation UITableView (placeHolder)
//  每个对象实例化的时候都户调用 load. 所以我们在 load 里面进行方法交换. 利用dispatch_once_t 保证只交换一次
+(void)load
{
   //保证只交换一次
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       //原来的方法
       Method orginMethod = class_getInstanceMethod(self, @selector(reloadData));
       //现在的方法
       Method currentMethod = class_getInstanceMethod(self, @selector(currentReloadData));
       //方法交换
       method_exchangeImplementations(orginMethod, currentMethod);
   });

}

-(void)currentReloadData {
   //调用之前的方法  这个时候已经交换了,下面的代码实际上调用的是 reloadData
   [self currentReloadData];
   //设置 placeholder
   [self setDefalutView];
}

//设置 placeHolder 获取 section 和 row  进行一个判断. 
-(void)setDefalutView {
   id<UITableViewDataSource> dataSource = self.dataSource;
   NSInteger section = 0;
   if ([dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
       section = [dataSource numberOfSectionsInTableView:self];
   } else {
       section = 1;
   }
   
   NSInteger rows = 0;
   for (NSInteger i = 0; i < section; i++) {
       NSInteger num = [dataSource tableView: self numberOfRowsInSection:section];
       if (num != 0) {
           rows = num;
       }
   }
   //如果是空的则添加
   if (!rows) {
       if (self.placeHolder && ![self.placeHolder isDescendantOfView:self]) {
           [self addSubview:self.placeHolder];
       }
       self.placeHolder.hidden = NO;
       self.placeHolder.frame = self.bounds;
   } else {
       if (self.placeHolder) {
           self.placeHolder.hidden = YES;
       }
   }
   
}

NSInteger num = 999;
   
//添加属性
-(UIView *)placeHolder
{
   return  objc_getAssociatedObject(self, &num);
}

-(void)setPlaceHolder:(UIView *)placeHolder
{
   objc_setAssociatedObject(self, &num, placeHolder, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

//
//  UITableView+placeHolder.h
//  test2
//
//  Created by liuyaozong on 2021/8/15.
//

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UITableView (placeHolder)
@property (strong,nonatomic) UIView *placeHolder;
@end

NS_ASSUME_NONNULL_END


实际使用

//
//  ViewController.m
//  test2
//
//  Created by liuyaozong on 2021/8/15.
//

#import "ViewController.h"
#import "UITableView+placeHolder.h"
@interface ViewController ()<UITableViewDelegate,UITableViewDataSource>
@property (strong,nonatomic) UITableView *tableView;
@property (strong,nonatomic) UIButton *changeStatusBtn;
@property (strong,nonatomic) NSMutableArray *dataList;
@end

@implementation ViewController
-(UIButton *)changeStatusBtn
{
    if (!_changeStatusBtn) {
        _changeStatusBtn = [[UIButton alloc] init];
        [_changeStatusBtn setTitle:@"切换状态" forState:UIControlStateNormal];
        [_changeStatusBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [_changeStatusBtn addTarget:self action:@selector(clickChange) forControlEvents:UIControlEventTouchUpInside];
    }
    return  _changeStatusBtn;
}
-(NSMutableArray *)dataList
{
    if (!_dataList) {
        _dataList = [NSMutableArray array];
    }
    return  _dataList;
}

-(UITableView *)tableView
{
    if (!_tableView) {
        _tableView = [[UITableView alloc] init];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"];
        _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        UILabel *label = [[UILabel alloc] init];
        label.text = @"暂无数据";
        label.textColor = [UIColor redColor];
        label.textAlignment = NSTextAlignmentCenter;
        _tableView.placeHolder = label;
    }
    return _tableView;
}



- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self.view addSubview:self.tableView];
    [self.view addSubview:self.changeStatusBtn];
    
    dispatch_async(dispatch_get_main_queue(), ^{
        self.tableView.frame = self.view.bounds;
        self.changeStatusBtn.frame = CGRectMake(100, 100, 100, 100);
        [self.tableView reloadData];
    });
}

-(void)clickChange
{
    if (self.dataList.count == 0) {
        NSArray *arr = @[@"1",@"1",@"1",@"1",@"1"];
        [self.dataList addObjectsFromArray:arr];
    } else {
        [self.dataList removeAllObjects];
    }
    [self.tableView reloadData];
}


- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];
    cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row];
    return  cell;
    
}

- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataList.count;
}



@end

相关文章

网友评论

    本文标题:iOS 高级开发(2)之 Runtime 方法交换

    本文链接:https://www.haomeiwen.com/subject/focubltx.html