RunTime黑科技

RunTime黑科技

1.方法的拦截(hook大法)

(1)可能用到的方法:

a.获取类方法:

Method class_getClassMethod(Class cls, SEL name)

b.获取实例方法:

Method class_getInstanceMethod(Class cos, SEL name)

c.方法实现部分的更换

void method_exchangeImplementations(Method m1, Method m2)

(2)应用场景
我们有时会遇到这么的需求,比如:在页面里使用了UINavigationController嵌套的UIViewController,但页面推过去的时候,由于上一级页面的title太长了,怎么办?我们一般情况之下都会在UIViewController的ViewDidLoad方法里去手动设置它:self.navigationItem.leftBarButtonItem = ...,这样的做法是可以,但是如果要在之上设置返回item的对应的title的话如何呢,按这种做法我们需要自定义一个CustomView出来,这样才可以设置它的title。但是这里我介绍一个更好的方法可以设置返回item的title:即捕获系统的UIViewController的-(void)setTitle:(NSString *)title方法:

上代码声明部分:

#import <Foundation/Foundation.h>

@interface SystemMethodHook : NSObject

/**
 * 捕获 系统的setTitle方法
 */
+ (void)hookCustomBackTitle;

@end

实现部分:

#import "SystemMethodHook.h"
#import <objc/runtime.h>
#import <UIKit/UIKit.h>

@interface UIViewController(Hook)

- (void)setCustomTitle:(NSString *)customTitle;

@end

@implementation UIViewController(Hook)

- (void)setCustomTitle:(NSString *)customTitle
{
    //这里判断UIViewController的所要设置的title长度是否大于5
    if ([self isKindOfClass:[UIViewController class]] && customTitle.length > 5)
    {
        UIBarButtonItem * backButtonItem = [[UIBarButtonItem alloc] init];
        backButtonItem.title = @"后退";
        //backBarButtonItem替换
        self.navigationItem.backBarButtonItem = backButtonItem;
    }
    //未进入判断时,这里其实再次用了hook方法替换(即@selector(setCustomTitle:)->@selector(setTitle:))
    [self setCustomTitle:customTitle];
}

@end

@implementation SystemMethodHook

+ (void)hookCustomBackTitle
{
    Method m1 = class_getInstanceMethod([UIViewController class], @selector(setTitle:));
    Method m2 = class_getInstanceMethod([UIViewController class], @selector(setCustomTitle:));
    method_exchangeImplementations(m1, m2);
}

@end

这样我们只需要在设置调用self.title = ...- (void)setTitle:(NSString *)title;方法之前,调用[SystemMethodHook hookCustomBackTitle];就可以避免返回item的标题太长的情况了。如果要让所有UIViewController都能实现,需要让[SystemMethodHook hookCustomBackTitle] 的调用尽可能靠前,比如放到AppDelegate的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions ; 最开头。

==========

2.分类添加属性

我们都知道OC的分类是不能够添加属性的,因为分类里面不能够生成对应的实例变量。但是OC是有动态特性的语言,它的动态特性就来源于Runtime,所以我们可以利用Runtime对象的关联存储(即set方法将参数值用一个KEY值关联一个对象保存起来,而get方法就在关联的对象中用KEY值去取这个保存的值)来实现分类添加属性的功能。

===============
1.需要用到的方法:

a. void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)

set方法,将值value 跟对象object 关联起来(将值value 存储到对象object 中)
参数 object:给哪个对象设置属性
参数 key:一个属性对应一个Key,将来可以通过key取出这个存储的值,key 可以是任何类型:double、int 等,建议用char 可以节省字节
参数 value:给属性设置的值
参数policy:存储策略 (assign 、copy 、 retain就是strong)

b.id objc_getAssociatedObject(id object , const void *key)

利用参数key 将对象object中存储的对应值取出来

===============

2.代码:
声明部分Person+Ext.h:

#import "Person.h"

typedef enum {
    EGenderUnknown,
    EGenderMale,
    EGenderFemale
}EPersonGender;

@interface Person (Ext)

@property (nonatomic, assign) EPersonGender gender;

@end

实现部分Person+Ext.m:

#import "Person+Ext.h"
#import <objc/runtime.h>


@implementation Person (Ext)

char key;
- (void)setGender:(EPersonGender)gender
{
    objc_setAssociatedObject(self, &key, @(gender), OBJC_ASSOCIATION_ASSIGN);
}

- (EPersonGender)gender
{
    return [objc_getAssociatedObject(self, &key) intValue];
}


@end

这样就在分类中添加了一个属性

3.动态添加方法的实现部分

即在类里不实现方法,外部给出实现(这种方式可能不会在项目里出现)

(1).需要用到的方法

OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) ;
    IMP myIMP = imp_implementationWithBlock(^(id _self, NSString *string) {
        NSLog(@"Hello %@", string);
    });
    class_addMethod([Sark class], @selector(sayHello:), myIMP, "v@:@");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值