runtime MethodSwizzle 实践之扩展 NIAttributedLabel
runtime MethodeSwizzle 提供 简单的方法交换已知类的 Method IMP.
Method 可以是 外部可访问的 public 或者 private Method .所谓的属性或私有变量 也不过是 getter/setter Method 而已。
MethodeSwizzle 技术 几乎可以实现你要使用 已知类的所有东西。
so Powerful。
代码实现:
- #import <Foundation/Foundation.h>
- @interface NSObject (Swizzle)
- + (void)swizzleInstanceSelector:(SEL)originalSelector
- withNewSelector:(SEL)newSelector;
- @end
- #import "NSObject+Swizzle.h"
- #import <objc/runtime.h>
- @implementation NSObject (Swizzle)
- + (void) swizzleInstanceSelector:(SEL)originalSelector
- withNewSelector:(SEL)newSelector
- {
- Method originalMethod = class_getInstanceMethod(self, originalSelector);
- Method newMethod = class_getInstanceMethod(self, newSelector);
- BOOL methodAdded = class_addMethod([self class],
- originalSelector,
- method_getImplementation(newMethod),
- method_getTypeEncoding(newMethod));
- if (methodAdded) {
- class_replaceMethod([self class],
- newSelector,
- method_getImplementation(originalMethod),
- method_getTypeEncoding(originalMethod));
- } else {
- method_exchangeImplementations(originalMethod, newMethod);
- }
- }
- @end
考虑通用性,这里使用NSObject 分类实现。
MethodeSwizzle 应用之解决实际问题:
最近使用
NIAttributedLabel 实现 文本渲染,图文混排等功能。还是挺不错的。
它提供简单的方法实现 插入文本链接, 设置delegate 回调 处理链接动作。
NIAttributedLabel.m 内部实现,
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
并在
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
中检测是否触发链接,并触发回调。
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
- [super touchesEnded:touches withEvent:event];
- [self.longPressTimer invalidate];
- self.longPressTimer = nil;
- UITouch* touch = [touches anyObject];
- CGPoint point = [touch locationInView:self];
- if (nil != self.originalLink) {
- if ([self isPoint:point nearLink:self.originalLink]) {
- // This old-style method is deprecated, please update to the newer delegate method that supports
- // more data types.
- NIDASSERT(![self.delegate respondsToSelector:@selector(attributedLabel:didSelectLink:atPoint:)]);
- if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectTextCheckingResult:atPoint:)]) {
- [self.delegate attributedLabel:self didSelectTextCheckingResult:self.originalLink atPoint:point];
- }
- }
- }
- self.touchedLink = nil;
- self.originalLink = nil;
- [self setNeedsDisplay];
- }
其中
- if (nil != self.originalLink) {
- if ([self isPoint:point nearLink:self.originalLink]) {
其中
- self.originalLink 用方法
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- - (NSTextCheckingResult *)linkAtPoint:(CGPoint)point {
- if (!CGRectContainsPoint(CGRectInset(self.bounds, , -kVMargin), point)) {
- return nil;
- }
- CFArrayRef lines = CTFrameGetLines(self.textFrame);
- if (!lines) return nil;
- CFIndex count = CFArrayGetCount(lines);
- NSTextCheckingResult* foundLink = nil;
- CGPoint origins[count];
- CTFrameGetLineOrigins(self.textFrame, CFRangeMake(,), origins);
- CGAffineTransform transform = [self _transformForCoreText];
- CGFloat verticalOffset = [self _verticalOffsetForBounds:self.bounds];
- for (int i = ; i < count; i++) {
- CGPoint linePoint = origins[i];
- CTLineRef line = CFArrayGetValueAtIndex(lines, i);
- CGRect flippedRect = [self getLineBounds:line point:linePoint];
- CGRect rect = CGRectApplyAffineTransform(flippedRect, transform);
- rect = CGRectInset(rect, , -kVMargin);
- rect = CGRectOffset(rect, , verticalOffset);
- if (CGRectContainsPoint(rect, point)) {
- CGPoint relativePoint = CGPointMake(point.x-CGRectGetMinX(rect),
- point.y-CGRectGetMinY(rect));
- CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
- foundLink = [self linkAtIndex:idx];
- if (foundLink) {
- return foundLink;
- }
- }
- }
- return nil;
- }
获得。
- 这两个条件成立,则触发链接,否则就返回了。??
- 实际情况可能是 我要检测 链接是否触发,没有触发的话我要自定义动作。
- 而且这两个方法还都是 NIAttributedLabel 类得私有方法, 举步维艰之际想到了强大的MethodSwizzle
思路:在分类中 定义两个函数 然后分别与 NIAttributedLabel 中的以上两个方法 调换。
- #import "NIAttributedLabel+XYNIAttributedLabel.h"
- #import <objc/runtime.h>
- #import "NSObject+XYSwizzle.h"
- @implementation NIAttributedLabel (XYNIAttributedLabel)
- +(void)load{
- [self swizzleInstanceSelector:@selector(linkAtPoint:) withNewSelector:@selector(swizzleLinkAtPoint:)];
- [self swizzleInstanceSelector:@selector(isPoint:nearLink:) withNewSelector:@selector(swizzleIsPoint:nearLink:)];
- }
- -(BOOL)isTriggerLink:(CGPoint )point{
- NSTextCheckingResult *textCheckingResult = [self swizzleLinkAtPoint:point];
- if (nil != textCheckingResult) {
- if ([self swizzleIsPoint:point nearLink:textCheckingResult]) {
- return YES;
- }
- }
- return NO;
- }
- -(NSTextCheckingResult *)swizzleLinkAtPoint:(CGPoint)point{
- return [self swizzleLinkAtPoint:point];
- }
- -(BOOL)swizzleIsPoint:(CGPoint)point nearLink:(NSTextCheckingResult *)link{
- BOOL resulte = [self swizzleIsPoint:point nearLink:link];
- return resulte;
- }
- @end
:上面
- +(void)load 方法中 linkAtPoint 、isPoint:nearLink: 有可能会报编译器警告。无法找到相关sel ,因为它们是私有方法。不要理他,这个是在runtime 生效。
我在demo 里有警告,但到了项目里好像没有了。不管它吧。
并提供
-(BOOL)isTriggerLink:(CGPoint )point; 对外调用 检测是否触发链接。
so。 问题得意 轻松解决。
废话一句: 实例方法 在 类对象中保持。
- +(void)load{
- [self swizzleInstanceSelector:@selector(linkAtPoint:) withNewSelector:@selector(swizzleLinkAtPoint:)];
- [self swizzleInstanceSelector:@selector(isPoint:nearLink:) withNewSelector:@selector(swizzleIsPoint:nearLink:)];
- }
注: 以上解决方案有一定风险,目前支持NimbusKit-AttributedLabel (1.0.0)
,如果被交换的NIAttributedLabel 方法名字被作者修改,项目又重新更新了库,则没有效果。
以下是新增内容:
- * 新增功能:
- *
- * ,检测是否触发链接
- *
- * ,检测是否触发图片链接(原库中包含添加图文混排的方法,但如果没有链接文本,NIAttributedLabel 将会关闭用户交互)
- *
- * ,判断NIAttributedLabel 是否包含图片
- *
- * ,插入的图片支持图片链接,且可自定义触发图片链接的回调block
- *
- * ,支持图片链接、文字链接 混用且数量不限,可以准确定位触发源并自定义block 回调处理
demo 可以这里下载:git clone https://github.com/githhhh/Test_Pod.git
runtime MethodSwizzle 实践之扩展 NIAttributedLabel的更多相关文章
- runtime MethodSwizzle 实践之 奇怪crash : [UIKeyboardLayoutStar release]: message sent to deallocated instance
情景: 使用MethodSwizzle 实现对数组.字典 等系统方法的安全校验.显然能达到预期效果,但实际发现当 键盘显示的情况下 home app 进入后台,再单击app 图标 切换回前台时 发 ...
- 【kudu pk parquet】runtime filter实践
已经有好一阵子没有写博文了,今天给大家带来一篇最近一段时间开发相关的文章:在impala和kudu上支持runtime filter. 大家搜索下实践者社区,可以发现前面已经有好几位同学写了这个主题的 ...
- Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进
前言 调试之前请先关闭Favicon配置 spring: favicon: enabled: false 不然会发现有2个请求(如果用nginx+ 浏览器调试的话) 序列化工具类[ ...
- iOS Runtime 实践(1)
很多时候我们都在看iOS开发中的黑魔法——Runtime.懂很多,但如何实践却少有人提及.本文便是iOS Runtime的实践第一篇. WebView 我们这次的实践主题,是使用针对接口编程的方式,借 ...
- 领域驱动设计和实践(转:http://kb.cnblogs.com/page/112298/)
引言 软件系统面向对象的设计思想可谓历史悠久,20世纪70年代的Smalltalk可以说是面向对象语言的经典,直到今天我们依然将这门语言视为面向对象语言的基础.随着编程语言和技术的发展,各种语言特性层 ...
- DDD领域驱动设计和实践(转载)
-->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...
- (转)EntityFramework之领域驱动设计实践
EntityFramework之领域驱动设计实践 - 前言 EntityFramework之领域驱动设计实践 (一):从DataTable到EntityObject EntityFramework之领 ...
- 翻译-高效DevOps的10项实践
原文链接: http://www.drdobbs.com/architecture-and-design/top-10-practices-for-effective-devops/240149363 ...
- 高效DevOps的10项实践
高效DevOps的10项实践 原文链接: http://www.drdobbs.com/architecture-and-design/top-10-practices-for-effective-d ...
随机推荐
- jquery返回页面顶部
1.此博文图片样式引用腾讯网站,效果如下: 2.样式设置: #toTop { /*选中的背景图片的大小*/ width: 54px; height: 54px; display: none;/*刚开始 ...
- 【noip模拟赛4】Matrix67的派对 dfs
描述 Matrix67发现身高接近的人似乎更合得来.Matrix67举办的派对共有N(1<=N<=10)个人参加,Matrix67需要把他们安排在圆桌上.Matrix67的安排原则是,圆桌 ...
- Free DIY Tour HDU1224
一道很好的dfs加储存路径的题目 :路径保存:每次dfs都存i 当大于max时 将临时数组保存到答案数组 并不是当 当前值大于最大值时更新路径 还要加上一个条件:能回去 #include<bi ...
- poj3259 Wormholes (判负环)【spfa】(模板)
<题目链接> 题目大意: John的农场里N块地,M条路连接两块地,W个虫洞,虫洞是一条单向路,会在你离开之前把你传送到目的地,就是当你过去的时候时间会倒退Ts.我们的任务是知道会不会在从 ...
- go语言学习-goroutine
o 语言有一个很重要的特性就是 goroutine, 我们可以使用 goroutine 结合 channel 来开发并发程序. 并发程序指的是可以同时运行多个任务的程序,这里的同时运行并不一定指的是同 ...
- Spark函数式编程进阶
函数式编程进阶 1.函数和变量一样作为Scala语言的一等公民,函数可以直接复制给变量: 2.函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是匿名函数赋值给 ...
- 【Github教程】:github入门到精通
[初识Github] 首先让我们大家一起喊一句"Hello Github".YEAH!就是这样. 原文 http://www.eoeandroid.com/thread-27455 ...
- BZOJ4313 : 三维积木
不妨设$R$是唯一可以看到的颜色,考虑一维序列的情况. 设$f[i][j][k][x][y]$表示考虑了前$i$个位置,第$i$个位置的高度是$j$,最高高度是$k$,已经用了$x$个$R$,$y$个 ...
- spring boot + embed tomcat + standalone jar的内存泄露问题
前一阵遇到了一个很坑的内存泄露问题,记录于此: 有个项目采用spring cloud重构后,部署到线上(其中有一个接口,大概每天调用量在1千万次左右),发现zabbix监控里,linux的可用内存一直 ...
- Makefile 中的.PHONY
PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字.有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能. 所谓的PHONY这个单词就是伪造的意思,makefile中将.PH ...