runtime(二)
前言
上一篇中我们大致的了解了runtime的一些基本概念,这一篇我们一起来看看如何使用它。
3、如何使用runtime。
3.1 方法交换
举一个老生常谈的例子。当你接手一个新的项目,需要查看这个程序运行时出现的UI对应的控制器的时候,如果你单纯的去通过UI上面的关键词全局搜索或是在viewwillappear里面打印该类的类名,你会浪费很长的时间,而且你会很难受。这个时候用runtime就会很好处理这种情况。
UI出现的时候,都会调用方法viewWillAppear。也就是说,如果我能让程序统一不走这个方法,走另一个方法(假设名字是myViewWillAppear),而这个方法内部还是会调用viewWillAppear,那么我就可以在myViewWillAppear中打印将要出现的UI的类名。看看下面的代码:
#import "UIViewController+WillAppear.h"
#import <objc/runtime.h> @implementation UIViewController (WillAppear)
+ (void)load{
Method method1 = class_getInstanceMethod([self class], @selector(viewWillAppear:));
Method method2 = class_getInstanceMethod([self class], @selector(logViewWillAppear:));
method_exchangeImplementations(method1, method2);
} - (void)logViewWillAppear:(BOOL)animated{
NSString *className = NSStringFromClass([self class]);
if ([className hasPrefix:@"LMF"]) {
NSLog(@"%@ will appear", className);
}
[self logViewWillAppear:animated];
}
@end
将系统的viewwillappear的指针与logviewwillappear的指针交换,这样,UI出现的时候会先调用logviewwillappear然后再调用viewwillappear方法,目的便达到了。
以此类推,我们有时会碰到unrecognized selector这种错误,这种错误很低级,但确实存在。对于初级开发者来讲时常会碰到,这个时候如果用runtime检测对象是否实现了此方法判断是否需要将该方法替换成其他事先已经实现的方法,可以避免这种错误。不过,推荐别这样玩,因为bug暴露越早越好,这里只是谈一下runtime解决这种问题的可行性。
3.2 可拓展性(动态添加方法和属性)
讲道理,我们还是要遵守一下可拓展性的。谁都不希望接手一份处处都要修改源码的代码,这个时候如果拓展性高,在不改变类内部源码的情况下给这个类添加额外的方法或者属性无疑是最好的。正好,oc的runtime很好的提供了这种特性。
其实我们一直都在用runtime,category就是最好的证明。它可以帮助我们给某些类在不需要修改该类内部的源码的情况下添加额外的方法,我们形象的称之为类别。类别怎么写我就不说了,你不会写算我输。添加方法用类别,那么添加属性能不能用类别呢?非常遗憾,oc没有这种语法。这个时候,还是用到runtime,看看下面代码,就懂了怎么添加:
objc_setAssociatedObject([UIApplication sharedApplication].delegate, @"USEROperationUnReadCount", [NSNumber numberWithInteger:count], OBJC_ASSOCIATION_RETAIN); objc_getAssociatedObject([UIApplication sharedApplication].delegate, @"USEROperationUnReadCount");
上述代码是给AppDelegate动态添加了一个属性叫USEROperationUnReadCount,值是个NSNumber对象,OBJC_ASSOCIATION_RETAIN表示这个属性的内存管理模式。get方法是将这个新加的属性值取出来,这样形成一个完整的结构。
通过类别动态的添加方法是非常方便的,这里还说一种比较不常用的方式,不推荐使用。话不多说,直接上代码。
.m中代码:
#import "LMFView.h"
#import <objc/runtime.h>
void show(id self, SEL _cmd, NSString *message){
NSLog(@"%@", message);
}
void eat(id self, SEL _cmd, NSString *who){
NSLog(@"%@ eat meat", who);
}
@implementation LMFView + (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(show)) {
class_addMethod([self class], sel, (IMP)show, "v@:@:");
return YES;
}else if (sel == @selector(eat)) {
class_addMethod([self class], sel, (IMP)eat, "v@:@:");
return YES;
}
return [super resolveInstanceMethod:sel];
} @end
.h中代码:
#import <UIKit/UIKit.h> @interface LMFView : UIView @end
我们可以看到,并没有声明方法eat。然后我们在外部调用eat,当然不能直接通过[]调用,因为编译通不过。用选择器调用:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor whiteColor];
self.lmfView = [[LMFView alloc] initWithFrame:CGRectMake(, , , )];
self.lmfView.backgroundColor = [UIColor redColor];
[self.view addSubview:self.lmfView]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showNextView)];
[self.lmfView addGestureRecognizer:tap]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(, , , );
button.backgroundColor = [UIColor cyanColor];
[button setTitle:@"runtime" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button addTarget:self action:@selector(show) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
} - (void)show
{
[self.lmfView performSelector:@selector(eat) withObject:@"runtime(二)的更多相关文章
- <runtime> 的 <assemblyBinding> 元素
一.<assemblyBinding> 元素 包含有关程序集版本重定向和程序集位置的信息. <assemblyBinding xmlns="urn:schemas-micr ...
- HeadFirst设计模式之装饰者模式
一. 1.The Decorator Pattern attaches additional responsibilities to an object dynamically.Decorators ...
- [转] 使用maven运行java main的三种方式
原文地址: http://blog.csdn.net/qbg19881206/article/details/19850857?utm_source=tuicool&utm_medium=re ...
- 自定义xUtils框架
xUtils是基于Afinal开发的目前功能比较完善的一个Android开源框架,最近又发布了xUtil3.0,在增加新功能的同时又提高了框架的性能.它的功能很强大,但是有时候我们只需要其中的一些功能 ...
- .NetCore程序发布到IIS上面
一.概述 在传统的.NET Framework中,ASP.NET程序发布到IIS上面,是由IIS的工作进程(w3wp.exe)托管的,在任务管理器中可以找到该进程.在ASP.NET Core程序中不再 ...
- Thinkphp5笔记五:配置data文件夹
如果你看项目下的各种文件,有种乱七八糟的感觉的话,你就可以进行以下配置. 配置data文件夹的,整理各种文件,让看起来舒服些. 一.设置runtime文件夹 index.php define('RUN ...
- signalR常见问题
一.安装signalR会对应安装自己的NewJson包,如果引用了含有不同NewJson包的dll组件,会造成版本不一致.必须在运行环境中指出使用目标版本. 问题截图: 解决方式: <runti ...
- Maven学习总结(五):maven命令的含义及用法
Maven有许多命令,不管是在命令行(1),还是在Myecplise10的Maven项目--右键Run As(2),还是IDEA的左下角--Maven Projects--Maven项目名--Life ...
- 使用Maven运行Java main的3种方式使用Maven运行Java main的3种方式
maven使用exec插件运行java main方法,以下是3种不同的操作方式. 一.从命令行运行 1.运行前先编译代码,exec:java不会自动编译代码,你需要手动执行mvn compile来完成 ...
随机推荐
- Certificates
Certificates Certificates 即 ”证书“,约等于通行证,申请证书是我们进行真机调试与发布的第一步.证书主要分为两类:Development证书用来开发和调试应用程序Produc ...
- PyCharm Python迁移项目
把整个项目文件迁移过去后,执行文件会报不能执行XX,系统找不到指定的文件. 此时把当前的这个文件名字改一下,再运行,修改提示的错误.等错误全部修改,可以正常运行后,再把文件名改回去
- DelphiXE8怎么使用调试模式(小米手机2)
需求:在开发Android程序时,大家一直是使用ShowMessage.其实XE是支持下断点的. 操作:小米手机2: 1.小米手机2用USB线,连到电脑上. 2.小米手机2-设置-关于手机-" ...
- 您的位置:首页 » IOS » iOS中全局悬浮按钮,类似IPhone中的AssistiveTouch iOS中全局悬浮按钮,类似IPhone中的AssistiveTouch
原文地址:http://blog.5ibc.net/p/86562.html 前提:当时看到别人写过这个类似AssistiveTouch的demo,但是有问题,第一改变不了位置.第二切换页面后无法使用 ...
- 跟着百度学PHP[15]-session回收机制
gc(Garbage Collection 垃圾回收) 在用户访问的时候会生成许多的临时session文件,顾名思义session回收机制就是用来删除这些临时文件的. session.gc_maxli ...
- SpringBoot+Shiro引起事务失效、错误原因、解决方法
一.问题今天发现用户注册的Service的事务并没有起到作用,再抛出一个RuntimeException后,并没有发生回滚,下面是调试步骤: 1.检查数据库的引擎是否是innoDB 2.启动类上是否加 ...
- VSCode 创建项目常用命令
对 http://www.bkjia.com/Asp_Netjc/1233276.html 的补充 1. 创建HelloWorld.Solutions目录并且在此目录中创建sln解决方案 例:dotn ...
- Storm学习笔记——简介
1. 简介 流式计算的历史 早在7.8年前诸如UC伯克利.斯坦福等大学就开始了对流式数据处理的研究,但是由于更多的关注于金融行业的业务场景或者互联网流量监控的业务场景,以及当时互联网数据场景的限制,造 ...
- 在windows下安装Redis步骤(收集)
一.下载windows版本的Redis 去官网找了很久,发现原来在官网上可以下载的windows版本的,现在官网以及没有下载地址,只能在github上下载,官网只提供linux版本的下载 官网下载地址 ...
- mogndb 慢查询
0 摘要 在MySQL中,慢查询日志是经常作为我们优化查询的依据,那在MongoDB中是否有类似的功能呢?答案是肯定的,那就是开启Profiling功能.该工具在运行的实例上收集有关MongoDB的 ...