59

OC转Swift,你需要换个思考方式

 5 years ago
source link: http://www.cocoachina.com/ios/20180710/24106.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

之前也有学过Swift,但并没有使用到项目中,现在随着Swift的完善以及三方开源SDK的支持越来越多,最近公司项目也决定采用OC+Swift混编的方式进行开发,一方面让项目更简洁、另一方面也满足了自我学习的快感。但实际开发中,Swift与OC的区别还是蛮大的,如果Swift不需要兼顾与OC的桥接、与OC共用CocoaTouch框架,那么可以说Swift将会是与OC截然不同的两种语言,前者诞生于2014年,后者诞生于上个世纪80年代,可以说代沟非常大了。所以这里列举一些个人觉得跟OC差异化比较大的地方,在使用Swift编程的时候,希望不要总是带着翻译OC代码的思想,而是多利用Swift提供的现代化的新特性。(本篇文章基于Swift 4.0)

对类型的严格要求

在写OC代码的时候,虽然一个类的属性、一个方法的参数等等,我们都会定义对应的类型,但实际上,在使用中并没有严格的去遵守,比如下面的代码:

- (int)addA:(int)a B:(int)b {
    return a+b;
}

虽然定义了参数和返回值都是int类型的,但我们可以传进去任意类型进行加法运算,比如Double类型。

但是Swift就相对严格很多了:

func add(a: Int, b: Int) -> (Int) {
    return a+b
}

let b: UInt = 20
self.add(a: 10, b: b)    // 报错,b是UInt,不是Int类型

就算将UInt传给Int,也是会报错的。所以请注意,Swift是强类型语言,强类型语言在编译阶段就为开发者排除了很大一部分的类型不匹配的错误。很多时候OC中可能修改了一个变量的类型,但可能忘了修改其他用到这个变量的地方,可能会导致unrecognized selector的错误抛出,Swift则在编译阶段就很大程度上避免了这类问题的发生。

可选值

Swift不仅对类型是否匹配有严格要求,还对一个类型是否有值格外严格。

var a: String?
var b: String = "b"

对于上面两个变量a和b,虽然他们都是String类型的,但实际上他们是不一样的,a属于可选String类型,b是String类型。如果想进行a+b的操作,则需要对a解包。所以在开发中,对a的处理就比较重要了:

// 确定不为空值的时候强制解包
print(a!)

// 不确定是不是空值的时候,进行可选绑定
if let aTemp = a {
   print(a)
}

// 使用空合运算符保证在解包失败的时候有一个备选值
print(a ?? "0")

虽然在Swift的官方教程中,对可选值的讲解已经很透彻了,但实际项目中,不管是服务器数据转model还是一些逻辑上的对数值的处理,都比教程中复杂的多,所以在处理可选值的时候,请格外注意。

闭包更方便了,请合理利用

OC中的Block的语法可以说是非常恶心的了,我每次写Block的时候,都会打开这样一个网站 How Do I Declare A Block in Objective-C ?,所以在实际开发中,很多人为了避免使用Block,在写回调的时候,都会使用代理模式,但代理模式写起来,代码量就多了很多。在Swift中,闭包的语法得到了很好的规范,下面的变量c就是一个闭包,参数、返回值、实现,都一目了然,在使用中比Block方便了太多太多。

var c = { ()->() in }

当然Swift的闭包在使用中也需要处理retain cycle,同时还要注意逃逸闭、非逃逸闭包的区别。

get/set方法

在Swift中,引入了存储属性、计算属性的概念,存储属性用于存储一个值,计算属性则提供get/set方法来对其他值进行操作,如果不好理解,可以类比OC的property关键字:

@property (nonatomic, copy) NSString *aStr;

OC中的property关键字自动的帮我们生成两个属性,一个是_aStr:存储属性->用于存储一个NSString,一个是aStr:计算属性->get方法返回_aStr、set方法对_aStr进行一些其他操作。所以来看一下没有property关键字的Swift的存储属性、计算属性(下面是错误的写法):

var name: String? {
        get {
            return name
        }
        set {
            name = newValue
        }
    }

如果写成上面的形式,编译器会报警告,因为name用在了自己的get/set方法,在运行中,因为name在自己的get/set方法中操作name(本质还是调用name的get/set方法),所以最后肯定都是以陷入死循环而告终。

那么跟OC相比,想实现理想的get/set方法,Swift中的这个计算属性name缺的是什么呢?那就是一个存储属性_name:

var _name: String?
var name: String? {
        get {
            return _name
        }
        set {
            _name = newValue
        }
    }

以上,就实现了跟OC一样的@property和get/set方法的重写。

严格的初始化

Swift对类的初始化非常严格,相比OC加入了很多限制:必要构造器、逐一构造器、便利构造器、默认构造器、指定构造器、可失败构造器。这些个概念确实需要好好区分一下,我也被绕的晕头转向的,只能没事多看看官方文档了。

参数的inout

OC中的方法,想要对自己的参数进行修改,直接修改就好了,但Swift不可以,你必须表明你的参数为inout才行:

// 参数需要标记为inout
func changeANum(a: inout Int) {
        a = 100
}

var a: Int = 10
// 传入参数的时候需要取地址
self.changeANum(a: &a)
print(a)

这种规定虽然在语法上麻烦了一点,但这无疑为一个函数的权限做了限定,可以防止某些从外面传来的参数被不小心修改了。

面向协议编程

OC中的Protocol因为支持可选方法、因为总被用于代理模式、因为OC不是强类型语言,导致了OC中的Protocol并没有发挥该有的作用。但Swift对Protocol做出了很好的支持:比如支持继承、支持结构体的遵守、支持默认实现等,所以Swift的出现,在iOS开发者中掀起了一股面向协议编程的热潮(虽然别的语言早就玩腻了),希望刚接触Swift的小伙伴可以改变下思想,去尝试下Swift下的面向协议编程。

泛型

Swift是强类型语言,当然这也引入了很多麻烦,但Swift很好的支持了泛型(虽然也是别的语言玩剩下的,但相比OC进步了很多),使用泛型可以很好的对一个方法、类等做一些多类型的支持、协议的限定等,下面的方法的参数可以是任何遵守了Equatable协议的变量:

func judgeEqual<T: Equatable>(a: T, b: T) -> Bool {
        if a==b {
            return true
        } else {
            return false
        }
    }

对类C语法的抛弃

Swift抛弃了传统C的++、--运算。抛弃了switch语句中的break,这让每个case执行完,不会因为case后面忘了写break而继续执行到下一个case。对我来说,我觉得这是好事,毕竟++、--很多时候会让人很迷惑到底是先运算还是后运算的?同时OC中的switch的break语句也曾经让我因为少写了一个break而让某个switch的两个case同时执行导致了项目中的bug。

String、Array、Dictionary都是结构体

Swift中的struct和class非常的像,如果想找出他们的最大区别,那肯定就是struct是值类型的、class是引用类型的。所以对于Swift中同是结构体的String、Array、Dictionary来说,在开发中就要与OC中的NSString、NSArray、NSDictionary区别对待了。毕竟值类型每次赋值都会将值赋值给新的一个变量,而引用类型每次赋值都是指向同一个引用。

虽然苹果为我们桥接了String和NSString,让我们可以在String和NSString间随意转换,但还是要注意他们的区别。同时OC中通过NSString和NSMutableString来区分不可变和可变字符串,Swift则通过let和var来区分不可变和可变。

重载运算符

Swift支持重载运算符,比如Swift支持"my "+"name "+"is " -> "my name is "这样的字符串加法操作,这让Swift比OC便利了很多,我们自己定义的类也支持运算符的重载,比如下面的类的“+”方法,则会合并两个model中的每个属性然后返回一个新的model:

class numModel {
        var aNum: Int = 0
        var bNum: Int = 0
        var cNum: Int = 0
        init(a: Int, b: Int, c: Int) {
            aNum = a
            bNum = b
            cNum = c
        }

        static func +(left: numModel, right: numModel) -> numModel {
            return numModel(a: left.aNum+right.aNum, b: left.bNum+right.bNum, c: left.cNum+right.cNum)
        }
    }

函数式编程

如果对RAC、RxSwift有点了解的话,会发现其中的map、filter、zip、reduce等方法用起来很方便,但实际上Swift的Sequence协议就提供了这些方法,Array、Dictionary也都支持这些方法,在开发中可以善于利用。

后语

写这篇文章的前一天晚上,突然想到想写这样一篇文章,记录自己在学习Swift的过程中碰到的一些点(坑点?),目前只想到以上列举的一些并写了出来,后续在开发、学习中遇到的一些问题,可能会单独拿出来写一篇文章,也可能继续补充到这一篇文章中。

如果以上写的东西,在理解上有误,非常感谢您能指出来让我知道,非常感谢。

作者:翻炒吧蛋滚饭

链接:https://www.jianshu.com/p/9da924d5683f


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK