OC工程下合理使用Swift为.m文件减负

一、文章背景

Swift ABI在5.0版本以后已经趋于稳定,可是公司现有工程还是以OC为主,直接将OC工程改写为Swift工程,无论从工作量还是实用性上来说都是不合理的。所以最佳方式还是混编。但是既然用到了Swift,就要把其优势体现出来。本文主要将得是利用Swift不同于OC独特的扩展功能来为OC的.m文件减负。

二、OC与Swift的扩展的区别

上面提到了本文主要利用Swift的扩展优势来优化OC工程代码,那么我们知道OC本身也有扩展,势必要比较一下两者的区别了。

  • OC扩展

.m文件顶端一般会有一段这样的代码

@interface 类名 ()
    
@end

这个最常见的代码实际上就是OC的一个私有扩展。 一般在这个私有扩展处添加私有成员变量、属性、声明方法等。
OC也可以创建一个公开的扩展文件,相信大家都会操作,这里就不演示了。同样其可以声明属性,方法等,但是扩展文件只有.h声明文件,没有.m实现文件。因为OC扩展的局限性,平常使用的确实很少。我总结的只有在SDK开发的时候,OC扩展的一个实用之处。

OC扩展的优势:
在SDK开发的时候,如果有需要其他内部类需要调用或使用的方法或属性,但是不想对SDK的使用者暴露,可以将这些方法或属性放置到其扩展中。打包SDK前,将扩展文件排为SDK的私有文件中

  • Swift扩展

Swift扩展相对于OC的扩展就厉害多了,本文就是使用Swift扩展的优势来优化OC代码的

Swift扩展的优势:

  1. Swift扩展可以把代码进行模块化区分,把功能性相同的代码放到一个扩展中
  2. Swift类的扩展里的方法、属性(一般为计算属性),在外部都可以使用,而且支持被继承
  3. Swift扩展可编写扩展属性或方法的实现功能

三、利用Swift扩展的优势为OC类减负

下面以一个案例来讲解一下用Swift减负的过程,这里就不介绍桥接过程了,如有疑问,可查看OC与Swift混编之桥接文件这篇文章。
我们先来看看未减负时类的代码结构:

原始OC类结构

  • 优先将XLLTestView类中的代理方法减负出去
    新建XLLTestView.swift文件,添加一个遵循collectionView delegate/dataSource等协议的XLLTestView类扩展,实现对应的代理方法。
extension XLLTestView: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout{
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 3
    }
    
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let itemHeight = collectionView.frame.size.height
        return CGSize(width: itemHeight * 101 / 125.0, height: itemHeight)
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        collectionView.register(XLLTestedCollectionViewCell.self, forCellWithReuseIdentifier: "XLLTestedCollectionViewCell")
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "XLLTestedCollectionViewCell", for: indexPath) as! XLLTestedCollectionViewCell
        cell.item = NSString(format: "%zd", (indexPath.item + 1) * 10)
        return cell
    }
}

extension XLLTestedView: 其他协议 {
    //对应的代理方法
    public func xxx() {
    }
}
  • XLLTestView中可脱离的私有方法(这里是将OC类中的控件布局的私有方法)在swift类扩展中实现,并使用@objc关键字
    请忽略swift布局使用了OC的Masnory库~代码显得略屌丝
@objc public extension XLLTestedView {
    //添加遍历构造函数,可选参数是自身参数为XLLTestView对象的尾随闭包,可选调用
    convenience init(with: ((XLLTestView) -> Void)? = nil) {
        self.init()
        with?(self)
    }
    //将OC类中的布局私有方法放入扩展中
    func setupSubviews() {
        self.addSubview(self.titleLabel)
        self.titleLabel.mas_makeConstraints { (make: MASConstraintMaker!) in
            make.top.equalTo()(kAutoSize(size: 20))
            make.centerX.equalTo()(0)
        }
        self.addSubview(self.collectionView)
        self.collectionView.mas_makeConstraints { (make: MASConstraintMaker!) in
            make.top.equalTo()(self.titleLabel.mas_bottom)?.offset()(kAutoSize(size: 35))
            make.left.right()?.equalTo()(0)
            make.width.equalTo()(kScreenWidth)
            make.bottom.equalTo()(kAutoSize(size: -70))
        }
        self.addSubview(self.sureBtn)
        self.sureBtn.mas_makeConstraints { (make: MASConstraintMaker!) in
            make.bottom.equalTo()(kAutoSize(size: -11))
            make.left.equalTo()(kAutoSize(size: 20))
            make.centerX.equalTo()(0)
            make.height.equalTo()(kAutoSize(size: 42))
        }
    }
    //可以增加其他一些OC能够调用的减负方法..
    func xxx() {
    }
}
  • 将隶属于XLLTestView的专用UI类XLLTestColectionViewCell放入Swift文件中实现
@objc class XLLTestCollectionViewCell: UICollectionViewCell {
    //MARK: life circle
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setupSubviews()
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    //MARK: private methods
    ...
    // MARK: setter
    ...
    //MARK:destruction methods
    ...
    //MARK: lazy loading
    ...
}
  • 将OC类中的一些@dynamic修饰的属性,需手动实现setter和getter方法,转为Swift的计算属性实现。
    原OC类@dynamic属性及实现
@property (nonatomic, copy, readonly) NSString *myInfo;

@dynamic myInfo;
- (NSString *)myInfo
{
    return @"CoderXLL";
}

将其转化至swift计算属性实现

@objc public extension XLLTestView {
    var myInfo: NSString {
        get {
            return "CoderXLL"
        }
    }
}

至此,基本上完成了对.m文件的减负工作。来看下减负后的代码结构。


减负后的结构

可以看到OC类已经减轻了很多压力,那些对业务逻辑关联不大的代码都已经移到对应的swift文件中去了,使得代码逻辑清晰了不少。

四、总结

其实总体来说代码并没有增多或减少,只是在OC工程下,望着愈渐臃肿的.m文件,这种方式自我感觉可以作为不错的一个应对方案。文章也只是以一个简单的案例进行了抛砖引玉。实际项目中,你肯定也会发现还有更多优化的空间。以上只是我本人的一次实践总结,如果你觉得有什么更好的建议或意见,欢迎留言,共同探讨
下面对文章的代码总结的一份demo,感兴趣的可以clone查看
Demo地址

你可能感兴趣的:(OC工程下合理使用Swift为.m文件减负)