MG--Swift3.0 好用的分类

作者: Mg明明就是你 | 来源:发表于2017-08-12 11:10 被阅读148次

    老司机,又到周末啦,平时我们工作中会写很多的工具类,但是有一些使用工具类去使用又不是很方便,我们就把它抽取封装成分类。来 看一下今天要写的关于手势的干货。

    • 以前我们是这样使用手势的:

    self.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapClick(tap:))))
    func tapClick(tap: UITapGestureRecognizer) {
          // 手势点击后要执行的代码  
    }
    
    • 现在用了UIGestureRecognizer+Block,简单来说,你可以这样使用 UIGestureRecognizer:

    self.messageLabel.addGestureRecognizer(UITapGestureRecognizer(actionBlock: { [unowned self](tap) in
          // 要执行的代码
     }))
    

    相比较来说:不再需要繁琐地使用 selector 去实现,也解决了代码分离的问题。就好像把delegate封装成了闭包,比如很多人使用的蓝牙框架第三方BabyBluetooth就是这么干的,它对系统的基于CoreBlueTooth的封装,内部把代理实现都转变成了Block形式。这样做的目的是代码封装性好,可读性强,易于维护。


    • 实现详情如下:

    //  封装的手势闭包回调
    import UIKit
    extension UIGestureRecognizer {
        typealias MGGestureBlock = ((Any)->())
        // MARK:- RuntimeKey   动态绑属性
        // 改进写法【推荐】用枚举实现绑定的key 写的时候会有提示
        fileprivate struct RuntimeKey {
            static let mg_GestureBlockKey = UnsafeRawPointer.init(bitPattern: "mg_GestureBlockKey".hashValue)
            /// ...其他Key声明
        }
        // 便利构造方法
        convenience init(actionBlock: @escaping MGGestureBlock) {
            self.init()
            addActionBlock(actionBlock)
            addTarget(self, action: #selector(invoke(_:)))
        }
        // 内部方法 加上fileprivat防止外界直接调用
        fileprivate func addActionBlock(_ block: MGGestureBlock?) {
            if (block != nil) {
                objc_setAssociatedObject(self, UIGestureRecognizer.RuntimeKey.mg_GestureBlockKey, block!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
        
        // 内部方法
        @objc fileprivate func invoke(_ sender: Any) {
            let block = objc_getAssociatedObject(self, UIGestureRecognizer.RuntimeKey.mg_GestureBlockKey) as? MGGestureBlock
            if (block != nil) {
                block!(sender);
            }
        }
    }
    
    • 原理就是把 block 动态地绑成 UIGestureRecognizer 的一个变量,invoke 的时候再调用这个 block。




    • 开发过程中遇到了的坑。

    • 我一开始在类方法里面进行了动态绑定,错误代码如下:
    //以下是错误代码:
    convenience init(actionBlock block: MGGestureBlock) {
        return self.init(target: self.gestureRecognizerBlockTarget(block), selector: #selector(self.invoke))
    }
    
    class func gestureRecognizerBlockTarget(_ block: MGGestureBlock) -> MGGestureRecognizerBlockTarget {
        var target: MGGestureRecognizerBlockTarget? = objc_getAssociatedObject(self, target_key)
        if target == nil {
            target = MGGestureRecognizerBlockTarget(block: block)
            objc_setAssociatedObject(self, target_key, target, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        return target!
    }
    

    这样导致的结果就是,变量被绑到了这个类对象上,因为在类方法里面 self 指的是这个类对象,而类对象是常驻内存直到程序退出才释放的,这也就导致了这个类上始终绑着第一次的 �target,之后无论怎样都不会改变。如果恰好你在 block 中有强制持有了 block 外的其他对象,那就会导致这些对象都不会释放,造成内存泄露。在实例方法中动态绑定即可解决。


    • 如果不使用动态绑定,使用如下的代码会产生怎样的结果?
    //错误代码
    convenience init(actionBlock block: MGGestureBlock) {
        return self.init(target: MGGestureRecognizerBlockTarget(block: block), selector: #selector(self.invoke))
    }
    

    结果就是出了这个作用域 target 对象释放。通过查阅文档,我在《OC 编程概念》的 Target-Action 一节中,看到苹果有提到这么一句: Control objects do not (and should not) retain their targets. 按照惯例,如果有特殊情况,苹果会特别提醒(比如 NSTimer 的描述中就写到 target The timer maintains a strong reference to this object until it (the timer) is invalidated. )。所以 UIGestureRecognizer 是不会对 target 强引用。一旦出了作用域,target 对象就释放了。所以,需要使用动态绑定强制持有。




    类似的UIButton也一样的

    extension UIButton {
        typealias MGButtonBlock = ((Any)->())
        // MARK:- RuntimeKey   动态绑属性
        // 改进写法【推荐】
        fileprivate struct RuntimeKey {
            static let mg_BtnBlockKey = UnsafeRawPointer.init(bitPattern: "mg_BtnBlockKey".hashValue)
            /// ...其他Key声明
        }
        
        convenience init(actionBlock: @escaping MGButtonBlock) {
            self.init()
            addActionBlock(actionBlock)
            addTarget(self, action: #selector(invoke(_:)), for: .touchUpInside)
        }
        
        fileprivate func addActionBlock(_ block: MGButtonBlock?) {
            if (block != nil) {
                objc_setAssociatedObject(self, UIButton.RuntimeKey.mg_BtnBlockKey, block!, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
        
        @objc fileprivate func invoke(_ sender: Any) {
            let block = objc_getAssociatedObject(self, UIButton.RuntimeKey.mg_BtnBlockKey) as? MGButtonBlock
            if (block != nil) {
                block!(sender);
            }
        }
        
        convenience init(imageName: UIImage, title: String,actionBlock: @escaping MGButtonBlock) {
            self.init(actionBlock: actionBlock)
            // 1.设置按钮的属性
            setImage(imageName, for: .normal)
            setTitle(title, for: UIControlState.normal)
            titleLabel?.font = UIFont.systemFont(ofSize: 14)
            setTitleColor(UIColor.darkGray, for: UIControlState.normal)
            sizeToFit()
        }
        
        convenience init(title: String,actionBlock: @escaping MGButtonBlock) {
            self.init(actionBlock: actionBlock)
            // 1.设置按钮的属性
            setTitle(title, for: UIControlState.normal)
            titleLabel?.font = UIFont.systemFont(ofSize: 14)
            setTitleColor(UIColor.darkGray, for: UIControlState.normal)
            sizeToFit()
        }
        
        convenience init(norImage:UIImage,pressImage: UIImage,actionBlock: @escaping MGButtonBlock) {
            self.init(actionBlock: actionBlock)
            // 1.设置按钮的属性
            setImage(norImage, for: .normal)
            setImage(pressImage, for: .highlighted)
        }
        
        convenience init(norImage:UIImage,selectedImage: UIImage,actionBlock: @escaping MGButtonBlock) {
            self.init(actionBlock: actionBlock)
            // 1.设置按钮的属性
            setImage(norImage, for: .normal)
            setImage(selectedImage, for: .selected)
        }
    }
    



    • 轻轻点击,关注我简书

    轻轻点击,关注我简书

    轻轻点击,关注我微博

    浏览我的GitHub


    • 扫一扫,关注我

    扫一扫,关注我.jpg

    相关文章

      网友评论

      • ZackDt:有个Closures框架,在swift比较好用,可以对比OC的BlocksKit

      本文标题:MG--Swift3.0 好用的分类

      本文链接:https://www.haomeiwen.com/subject/ixlrrxtx.html