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来完成 ...
随机推荐
- erlang四大behaviour之一gen_server(转载)
erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能用自己的module实现,这也就是behaviour了,应用behaviour可以减少与本身事务无关的代码量,设计 ...
- 0044 spring框架的applicationContext.xml的命名空间
Spring框架中,创建bean,装配bean,事务控制等,可以用xml配置或者注解扫描的方法实现.如果用注解扫描,在xml配置中得加上 <context:component-scan base ...
- word中特殊符号的替换
首先在word里替换快捷键是ctrl+H,点击“更多”,会出现更多选项,在特殊格式那里可以选在各种符号,比如回车,空格什么的. 有的时候在word里看不到一些格式,需要点一下下图的对着的两个箭头图标: ...
- java集成开发环境常用操作集
1.简单搭建maven集成开发环境 一. Jetty安装 下载地址(包涵windows和Linux各版本,Jetty9需要JDK7):http://download.eclipse.org/j ...
- LeetCode 145 Binary Tree Postorder Traversal(二叉树的兴许遍历)+(二叉树、迭代)
翻译 给定一个二叉树.返回其兴许遍历的节点的值. 比如: 给定二叉树为 {1. #, 2, 3} 1 \ 2 / 3 返回 [3, 2, 1] 备注:用递归是微不足道的,你能够用迭代来完毕它吗? 原文 ...
- windows config yii framework
download and config download yii-1.1.12 unzip into yii-1.1.12 config yii-1.1.12 dir an apache httpdo ...
- tomocat解决乱码问题
使用Tomcat进行JSP开发最头疼的莫过于中文乱码问题了,总结Tomcat乱码问题出现的原因必须明白以下几点: 1.Tomcat一般总是默认使用ISO-8859-1作为字符编码方式的.所以,除非你在 ...
- Asp.Net中使用水晶报表
Asp.Net中使用水晶报表(上) 在我们对VS.Net中的水晶报表(Crystal Reports)进行研究之前,我和我朋友对如何将这个复杂的东东加入我们的Web应用有着非常的好奇心.一周以后,在阅 ...
- 【PM面试题】设计一个股价推送工具
这一轮面试时间比较短,问题在短时间内不能很全面展开,因此抓住一些关键点变得尤其重要,这里我记录下当时是怎么想这个问题的. 问题解析 子问题1:推送什么?从问题中看出我们需要推送的是股价,用户可以自定义 ...
- 获取一个Assembly中的命名空间列表
通过System.Reflection.Assembly类中提供的方法和属性不能直接获取组件中的命名空间列表.但有方法可以直接获得Assembly中的所有类型,我们便可以通过获取的类型来得到命名空间名 ...