Objective-C 与 Swift 方法的类比系列(一)

以下内容来源于我的新blog idhong.com

新 blog 会不断更新,其他平台可能更新不及时哦。

1、单例

1.1、Objective-C 写法

<pre><code>

@interface IHUserManager : NSObject

  • (instancetype)sharedInstance;
    @end
    @implementation IHUserManager
  • (instancetype)sharedInstance {
    static IHUserManager *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
    }
    @end
    </code></pre>

Swift 写法

<pre><code>
import Foundation
class IHUserManager {
static let sharedInstance = IHUserManager()
}
</code></pre>

block和闭包在对象绑定时的坑

分析

oc 中,使用 runtime 的对象绑定相信大家都使用过,而且在 block 上的使用也经常看到,在 oc 中可以把 block 直接使用 runtime 通过 key 和其他的对象绑定起来,但在 swift 中闭包不是:AnyObject 类型 使用在进行对象绑定时无法进行。
参考:
1.stackoverflow.com
2、http://nshipster.cn
需要使用:
public func unsafeBitCast<T, U>(x: T, _: U.Type) -> U
函数

代码实现

对 UIControl 增加 Block 属性和 Closure 属性

swift 具体的实现如下:

<pre><code>
typealias IHClosure = @convention(block)() -> ()

extension UIControl {

private struct ClosureKey {
    static var touchUpInsideKey = "touchUpInsideKey"
}

func ih_addTouchUpInsideClosure(closure: IHClosure) {
    let obj: AnyObject = unsafeBitCast(closure, AnyObject.self)
    objc_setAssociatedObject(self, &ClosureKey.touchUpInsideKey, obj, .OBJC_ASSOCIATION_COPY_NONATOMIC)
    
    self.addTarget(self, action: #selector(UIControl._touchUpInside), forControlEvents: .TouchUpInside)
}

@objc private func _touchUpInside() {
    let obj: AnyObject = objc_getAssociatedObject(self, &ClosureKey.touchUpInsideKey)
    let closure: IHClosure = unsafeBitCast(obj, IHClosure.self)
    closure()
}

}
</code></pre>

Objective-C 具体的实现如下:

<pre><code>

import “UIControl+Block.h”

import <objc/runtime.h>

const char kTouchUpInside = ‘\0’;

@implementation UIControl (Block)

  • (void)ih_addTouchUpInsideWithBlock:(dispatch_block_t)block {
    objc_setAssociatedObject(self, @selector(_touchControlEvents), block, OBJC_ASSOCIATION_COPY_NONATOMIC);
    [self addTarget:self action:@selector(_touchControlEvents) forControlEvents:UIControlEventTouchUpInside];
    }

  • (void)_touchControlEvents {
    dispatch_block_t block = objc_getAssociatedObject(self, _cmd);
    if (block) block();
    }
    @end
    </code></pre>

swift 和 oc 之 load方法不同以及处理方案

分析

在 oc 开发中,APP统计时,自然会使用到 方法的替换,下面方法

OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

通常会为 vc 添加一个 category 同时在 load 方法里面的其做相应的处理。但在 swift 中系统不允许使用 load 方法。那么我们可以想办法使用:
initialize 方法

代码实现

oc 具体的实现如下:

<pre><code>

import “UIViewController+Swizzle.h”

import <objc/runtime.h>

@implementation UIViewController (Swizzle)

  • (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{

      // 获取系统的方法的 方法对象
      Method orignViewDidLoad = class_getInstanceMethod([UIViewController class], @selector(viewDidLoad));
      
      // 获取准备用来替换的 方法对象
      Method replacingMethod = class_getInstanceMethod([UIViewController class], @selector(_viewDidLoad_Swizzling));
    
    
      BOOL didAddMethod =  class_addMethod([self class], @selector(viewDidLoad), method_getImplementation(replacingMethod), method_getTypeEncoding(replacingMethod));
      
      if (didAddMethod) {
          // 替换一下方法地址
          class_replaceMethod([self class], @selector(_viewDidLoad_Swizzling), method_getImplementation(orignViewDidLoad), method_getTypeEncoding(orignViewDidLoad));
      }else{
          // 直接交换的调用
          method_exchangeImplementations(orignViewDidLoad, replacingMethod);
      }
    

    });
    }

  • (void)_viewDidLoad_Swizzling {
    [self _viewDidLoad_Swizzling];
    }
    @end
    </code></pre>

swift 具体的实现如下:

<pre><code>
extension UIViewController {

public override class func initialize() {

    struct Static {
        static var token: dispatch_once_t = 0
    }

    if self !== UIViewController.self {
        return
    }

    dispatch_once(&Static.token) {
        let originalSelector = #selector(UIViewController.viewWillAppear(_:))
        let swizzledSelector = #selector(UIViewController.nsh_viewWillAppear(_:))

        let originalMethod = class_getInstanceMethod(self, originalSelector)
        let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
        
        let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
        if didAddMethod {
            class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    }
}

func nsh_viewWillAppear(animated: Bool) {
    self.nsh_viewWillAppear(animated)
}

}
</code></pre>

参考资料

~😁欢迎转载、转载请注明出处.

    原文作者:liangdahong
    原文地址: https://www.jianshu.com/p/6dd53e485666
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞