runtime MethodeSwizzle 提供 简单的方法交换已知类的  Method IMP.

Method 可以是 外部可访问的 public 或者 private Method .所谓的属性或私有变量 也不过是 getter/setter Method 而已。

MethodeSwizzle 技术 几乎可以实现你要使用 已知类的所有东西。

so Powerful。

代码实现:

  1. #import <Foundation/Foundation.h>
  2.  
  3. @interface NSObject (Swizzle)
  4. + (void)swizzleInstanceSelector:(SEL)originalSelector
  5. withNewSelector:(SEL)newSelector;
  6. @end
  1. #import "NSObject+Swizzle.h"
  2. #import <objc/runtime.h>
  3.  
  4. @implementation NSObject (Swizzle)
  5. + (void) swizzleInstanceSelector:(SEL)originalSelector
  6. withNewSelector:(SEL)newSelector
  7. {
  8. Method originalMethod = class_getInstanceMethod(self, originalSelector);
  9. Method newMethod = class_getInstanceMethod(self, newSelector);
  10.  
  11. BOOL methodAdded = class_addMethod([self class],
  12. originalSelector,
  13. method_getImplementation(newMethod),
  14. method_getTypeEncoding(newMethod));
  15.  
  16. if (methodAdded) {
  17. class_replaceMethod([self class],
  18. newSelector,
  19. method_getImplementation(originalMethod),
  20. method_getTypeEncoding(originalMethod));
  21. } else {
  22. method_exchangeImplementations(originalMethod, newMethod);
  23. }
  24. }
  25. @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

中检测是否触发链接,并触发回调。

  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  3. [super touchesEnded:touches withEvent:event];
  4.  
  5. [self.longPressTimer invalidate];
  6. self.longPressTimer = nil;
  7.  
  8. UITouch* touch = [touches anyObject];
  9. CGPoint point = [touch locationInView:self];
  10.  
  11. if (nil != self.originalLink) {
  12. if ([self isPoint:point nearLink:self.originalLink]) {
  13. // This old-style method is deprecated, please update to the newer delegate method that supports
  14. // more data types.
  15. NIDASSERT(![self.delegate respondsToSelector:@selector(attributedLabel:didSelectLink:atPoint:)]);
  16.  
  17. if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectTextCheckingResult:atPoint:)]) {
  18. [self.delegate attributedLabel:self didSelectTextCheckingResult:self.originalLink atPoint:point];
  19. }
  20. }
  21. }
  22.  
  23. self.touchedLink = nil;
  24. self.originalLink = nil;
  25.  
  26. [self setNeedsDisplay];
  27. }

其中

  1. if (nil != self.originalLink) {
  2. if ([self isPoint:point nearLink:self.originalLink]) {
    其中
  1. self.originalLink 用方法
  1. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2. - (NSTextCheckingResult *)linkAtPoint:(CGPoint)point {
  3. if (!CGRectContainsPoint(CGRectInset(self.bounds, , -kVMargin), point)) {
  4. return nil;
  5. }
  6.  
  7. CFArrayRef lines = CTFrameGetLines(self.textFrame);
  8. if (!lines) return nil;
  9. CFIndex count = CFArrayGetCount(lines);
  10.  
  11. NSTextCheckingResult* foundLink = nil;
  12.  
  13. CGPoint origins[count];
  14. CTFrameGetLineOrigins(self.textFrame, CFRangeMake(,), origins);
  15.  
  16. CGAffineTransform transform = [self _transformForCoreText];
  17. CGFloat verticalOffset = [self _verticalOffsetForBounds:self.bounds];
  18.  
  19. for (int i = ; i < count; i++) {
  20. CGPoint linePoint = origins[i];
  21.  
  22. CTLineRef line = CFArrayGetValueAtIndex(lines, i);
  23. CGRect flippedRect = [self getLineBounds:line point:linePoint];
  24. CGRect rect = CGRectApplyAffineTransform(flippedRect, transform);
  25.  
  26. rect = CGRectInset(rect, , -kVMargin);
  27. rect = CGRectOffset(rect, , verticalOffset);
  28.  
  29. if (CGRectContainsPoint(rect, point)) {
  30. CGPoint relativePoint = CGPointMake(point.x-CGRectGetMinX(rect),
  31. point.y-CGRectGetMinY(rect));
  32. CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
  33. foundLink = [self linkAtIndex:idx];
  34. if (foundLink) {
  35. return foundLink;
  36. }
  37. }
  38. }
  39. return nil;
  40. }

获得。

  1. 这两个条件成立,则触发链接,否则就返回了。??
  2.  
  3. 实际情况可能是 我要检测 链接是否触发,没有触发的话我要自定义动作。
  4.  
  5. 而且这两个方法还都是 NIAttributedLabel 类得私有方法, 举步维艰之际想到了强大的MethodSwizzle

思路:在分类中 定义两个函数  然后分别与 NIAttributedLabel  中的以上两个方法 调换。

  1. #import "NIAttributedLabel+XYNIAttributedLabel.h"
  2. #import <objc/runtime.h>
  3. #import "NSObject+XYSwizzle.h"
  4. @implementation NIAttributedLabel (XYNIAttributedLabel)
  5.  
  6. +(void)load{
  7. [self swizzleInstanceSelector:@selector(linkAtPoint:) withNewSelector:@selector(swizzleLinkAtPoint:)];
  8. [self swizzleInstanceSelector:@selector(isPoint:nearLink:) withNewSelector:@selector(swizzleIsPoint:nearLink:)];
  9. }
  10. -(BOOL)isTriggerLink:(CGPoint )point{
  11. NSTextCheckingResult *textCheckingResult = [self swizzleLinkAtPoint:point];
  12. if (nil != textCheckingResult) {
  13. if ([self swizzleIsPoint:point nearLink:textCheckingResult]) {
  14. return YES;
  15. }
  16. }
  17. return NO;
  18. }
  19.  
  20. -(NSTextCheckingResult *)swizzleLinkAtPoint:(CGPoint)point{
  21. return [self swizzleLinkAtPoint:point];
  22. }
  23.  
  24. -(BOOL)swizzleIsPoint:(CGPoint)point nearLink:(NSTextCheckingResult *)link{
  25. BOOL resulte = [self swizzleIsPoint:point nearLink:link];
  26. return resulte;
  27. }
  28. @end

:上面

  1. +(void)load 方法中 linkAtPoint isPoint:nearLink: 有可能会报编译器警告。无法找到相关sel ,因为它们是私有方法。不要理他,这个是在runtime 生效。
    我在demo 里有警告,但到了项目里好像没有了。不管它吧。

并提供

-(BOOL)isTriggerLink:(CGPoint )point; 对外调用 检测是否触发链接。

so。 问题得意 轻松解决。

废话一句: 实例方法 在 类对象中保持。

  1. +(void)load{
  2. [self swizzleInstanceSelector:@selector(linkAtPoint:) withNewSelector:@selector(swizzleLinkAtPoint:)];
  3. [self swizzleInstanceSelector:@selector(isPoint:nearLink:) withNewSelector:@selector(swizzleIsPoint:nearLink:)];
  4. }

注: 以上解决方案有一定风险,目前支持NimbusKit-AttributedLabel (1.0.0)

,如果被交换的NIAttributedLabel 方法名字被作者修改,项目又重新更新了库,则没有效果。

以下是新增内容:

  1. * 新增功能:
  2. *
  3. * ,检测是否触发链接
  4. *
  5. * ,检测是否触发图片链接(原库中包含添加图文混排的方法,但如果没有链接文本,NIAttributedLabel 将会关闭用户交互)
  6. *
  7. * ,判断NIAttributedLabel 是否包含图片
  8. *
  9. * ,插入的图片支持图片链接,且可自定义触发图片链接的回调block
  10. *
  11. * ,支持图片链接、文字链接 混用且数量不限,可以准确定位触发源并自定义block 回调处理

demo 可以这里下载:git clone  https://github.com/githhhh/Test_Pod.git

runtime MethodSwizzle 实践之扩展 NIAttributedLabel的更多相关文章

  1. runtime MethodSwizzle 实践之 奇怪crash : [UIKeyboardLayoutStar release]: message sent to deallocated instance

    情景: 使用MethodSwizzle 实现对数组.字典 等系统方法的安全校验.显然能达到预期效果,但实际发现当 键盘显示的情况下  home app 进入后台,再单击app  图标 切换回前台时 发 ...

  2. 【kudu pk parquet】runtime filter实践

    已经有好一阵子没有写博文了,今天给大家带来一篇最近一段时间开发相关的文章:在impala和kudu上支持runtime filter. 大家搜索下实践者社区,可以发现前面已经有好几位同学写了这个主题的 ...

  3. Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进

    前言 调试之前请先关闭Favicon配置 spring:     favicon:       enabled: false 不然会发现有2个请求(如果用nginx+ 浏览器调试的话) 序列化工具类[ ...

  4. iOS Runtime 实践(1)

    很多时候我们都在看iOS开发中的黑魔法——Runtime.懂很多,但如何实践却少有人提及.本文便是iOS Runtime的实践第一篇. WebView 我们这次的实践主题,是使用针对接口编程的方式,借 ...

  5. 领域驱动设计和实践(转:http://kb.cnblogs.com/page/112298/)

    引言 软件系统面向对象的设计思想可谓历史悠久,20世纪70年代的Smalltalk可以说是面向对象语言的经典,直到今天我们依然将这门语言视为面向对象语言的基础.随着编程语言和技术的发展,各种语言特性层 ...

  6. DDD领域驱动设计和实践(转载)

    -->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...

  7. (转)EntityFramework之领域驱动设计实践

    EntityFramework之领域驱动设计实践 - 前言 EntityFramework之领域驱动设计实践 (一):从DataTable到EntityObject EntityFramework之领 ...

  8. 翻译-高效DevOps的10项实践

    原文链接: http://www.drdobbs.com/architecture-and-design/top-10-practices-for-effective-devops/240149363 ...

  9. 高效DevOps的10项实践

    高效DevOps的10项实践 原文链接: http://www.drdobbs.com/architecture-and-design/top-10-practices-for-effective-d ...

随机推荐

  1. jquery返回页面顶部

    1.此博文图片样式引用腾讯网站,效果如下: 2.样式设置: #toTop { /*选中的背景图片的大小*/ width: 54px; height: 54px; display: none;/*刚开始 ...

  2. 【noip模拟赛4】Matrix67的派对 dfs

    描述 Matrix67发现身高接近的人似乎更合得来.Matrix67举办的派对共有N(1<=N<=10)个人参加,Matrix67需要把他们安排在圆桌上.Matrix67的安排原则是,圆桌 ...

  3. Free DIY Tour HDU1224

    一道很好的dfs加储存路径的题目  :路径保存:每次dfs都存i 当大于max时 将临时数组保存到答案数组 并不是当 当前值大于最大值时更新路径 还要加上一个条件:能回去 #include<bi ...

  4. poj3259 Wormholes (判负环)【spfa】(模板)

    <题目链接> 题目大意: John的农场里N块地,M条路连接两块地,W个虫洞,虫洞是一条单向路,会在你离开之前把你传送到目的地,就是当你过去的时候时间会倒退Ts.我们的任务是知道会不会在从 ...

  5. go语言学习-goroutine

    o 语言有一个很重要的特性就是 goroutine, 我们可以使用 goroutine 结合 channel 来开发并发程序. 并发程序指的是可以同时运行多个任务的程序,这里的同时运行并不一定指的是同 ...

  6. Spark函数式编程进阶

    函数式编程进阶 1.函数和变量一样作为Scala语言的一等公民,函数可以直接复制给变量: 2.函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是匿名函数赋值给 ...

  7. 【Github教程】:github入门到精通

    [初识Github] 首先让我们大家一起喊一句"Hello Github".YEAH!就是这样. 原文 http://www.eoeandroid.com/thread-27455 ...

  8. BZOJ4313 : 三维积木

    不妨设$R$是唯一可以看到的颜色,考虑一维序列的情况. 设$f[i][j][k][x][y]$表示考虑了前$i$个位置,第$i$个位置的高度是$j$,最高高度是$k$,已经用了$x$个$R$,$y$个 ...

  9. spring boot + embed tomcat + standalone jar的内存泄露问题

    前一阵遇到了一个很坑的内存泄露问题,记录于此: 有个项目采用spring cloud重构后,部署到线上(其中有一个接口,大概每天调用量在1千万次左右),发现zabbix监控里,linux的可用内存一直 ...

  10. Makefile 中的.PHONY

    PHONY 目标并非实际的文件名:只是在显式请求时执行命令的名字.有两种理由需要使用PHONY 目标:避免和同名文件冲突,改善性能. 所谓的PHONY这个单词就是伪造的意思,makefile中将.PH ...