zoukankan      html  css  js  c++  java
  • Objective-C运行时的一些技巧

    Apple的iOS8发布以后,大家都开始了适配的工作了。但是这个过程总会遇到一些拦路虎,例如推送的API改动。可是商业项目上嵌入了各种各样的第三方静态库,这些静态库质量参差不齐,其中一个静态库甚至在Xcode6上编译后出现问题。于是只能使用Xcode5来编译,但这样就有一个很纠结的问题就是,UIMutableUserNotificationAction等一些类在旧版的Xcode要么就是无法编译,要么只能用宏来跳过。

    这时候,我还是想起了Objective-C的运行时方法,使用NSClassFromString(@"UIMutableUserNotificationAction")来获取到系统的类。光这样子还是有很多不足,因为这个类中有很多方法、属性。虽然保证了运行的正常,但是编写这些方法还是有各种不便。例如各种performSelector:、objc_msgSend、setValue: forKey:,实在写得很痛苦。我这里用了一个比较取巧的方法,新建一个伪造的类如“XXXMutableUserNotificationAction”,继承NSObject即可。然后将UIMutableUserNotificationAction所有的属性和方法的声明添加到XXXMutableUserNotificationAction的头文件。以后,使用UIMutableUserNotificationAction时,就如下方:

    1 Class XXXMutableUserNotificationActionClass = NSClassFromString(@"UIMutableUserNotificationAction");
    2 XXXUIMutableUserNotificationAction *action = [[XXXMutableUserNotificationActionClass alloc] init];

    既可以使用Xcode的补全提示,又可以通过编译。(如果大家有更好的方法,欢迎探讨探讨)

    Object-C运行时的方法固然强大,但是使用这些方法还是有一定的风险。例如静态分析对于一些运行时的问题是检查不出来的,这里我举一个内存泄漏的例子。我的项目中使用了一些运行时添加属性的方法,同时为一些View添加了block类型的属性。在使用的时候,不小心在block中直接使用了self,就会出现编译器无法检查的内存泄露。泄漏路径:VC->View->block->VC,形成了循环引用。这种泄漏相对隐蔽一些,但对于经常RAC的童鞋来说,可能已经练就到百毒不侵了(^_^)。因为@weakify和@strongify的频繁使用,我对这类型泄漏已经比较敏感。__weak typeof(self) weakSelf = self,算是一种虽然难看,但是行之有效的方法,如果有兴趣也可以参考RAC的解决方案,本质上也是一样的。
    题外话,BlockKit包含了一个很好用的分类“NSObject+BKAssociatedObjects”,可以用更友好的方法实现运行时添加属性,顺带一提bk_weaklyAssociateValue的实现思路相当巧妙,值得借鉴。

    最后一个技巧是关于RAC和系统API的一些关系,先来看看一下两段代码:

    1 UIGestureRecognizer *dismissKeyboardGR = [[UIGestureRecognizer alloc] init];
    2 [self.view addGestureRecognizer:dismissKeyboardGR];
    3 [[[self rac_signalForSelector:@selector(gestureRecognizer:shouldReceiveTouch:)
    4                  fromProtocol:@protocol(UIGestureRecognizerDelegate)]
    5   takeUntil:dismissKeyboardGR.rac_willDeallocSignal]
    6   subscribeNext:^(id x) {
    7       [Utils hideKeyboardInAllView];
    8   }];
    9 dismissKeyboardGR.delegate = self;
    1 UIGestureRecognizer *dismissKeyboardGR = [[UIGestureRecognizer alloc] init];
    2 [self.view addGestureRecognizer:dismissKeyboardGR];
    3 dismissKeyboardGR.delegate = self;
    4 [[[self rac_signalForSelector:@selector(gestureRecognizer:shouldReceiveTouch:)
    5                  fromProtocol:@protocol(UIGestureRecognizerDelegate)]
    6   takeUntil:dismissKeyboardGR.rac_willDeallocSignal]
    7   subscribeNext:^(id x) {
    8       [Utils hideKeyboardInAllView];
    9   }];

    先来看看这两段代码的区别只在于delegate的设置先后不一样,但这就造成了后一段代码在iOS6上就无法触发RAC里方法,iOS7上正常。为什么呢?

    这涉及到一个类似缓存的机制。平时,我们会习惯使用respondsToSelector:来检查一个对象或者类是否实现了对应的方法,但频繁调用respondsToSelector:会对性能有一定的影响。特别是UITableView的dataSource一些方法,调用频率很高的。因此,在设置delegate后,UIGestureRecognizer使用了respondsToSelector:检查了一次self是否gestureRecognizer:shouldReceiveTouch:的方法,然后把这个结果缓存起来。由于RAC也使用了类似Method Swizzling方法,因此在设置delegate以后再使用RAC的方法,UIGestureRecognizer也只读取了缓存,并不会再次检查,所以认为self并未实现gestureRecognizer:shouldReceiveTouch:的方法,于是不作调用。(具体缓存的实现方法,可以参照http://www.cnblogs.com/ipinka/p/3862786.html)

  • 相关阅读:
    模糊查询与索引
    weblogic 域的创建
    undo表空间缩小
    weblogic安装
    C#学习笔记——枚举类型
    C#学习笔记——windows窗体
    Qt之主窗口设计——打印图像
    C#学习笔记——MDI窗体(多文档界面)
    C#学习笔记——抽象类和抽象方法
    C#学习笔记——Main方法
  • 原文地址:https://www.cnblogs.com/ipinka/p/4039299.html
Copyright © 2011-2022 走看看