iOS 快速从OC过渡到Swift(4.1),由理论到实战

引言

本文旨在帮助开发者快速从OC开发过渡到Swift开发,挑选了一些比较浅显的但是比较常用的Swift语法特性,在介绍的过程中,通常会拿OC中的语言特性作比较,让大家更好的注意到Swift的不同。
另外需要说明的是,笔者也仅仅是刚刚接触Swift不久,如果有说的不对的地方,还望指正,这里贴出本文所参考的 Swift中文翻译地址,方便大家可以深入了解Swift。

本文发布时,Swift 刚出到 4.1 版本,目前已经发布 5.2 版本,文中的部分内容可能已经不适用,后期会给予修正。

Swift简介

Swift 是一门开发 iOS、macOC、watchOS 和 tvOS 应用的新语言,在初期,为了让 OC 开发者快速的过渡到 Swift 开发,Swift 继承了很多 C、OC 的语法特性,但是随着 Swift 的不断完善,已经慢慢自称一体,不仅保留了 OC 的很多语言特性,还借鉴了很多语言,如 C#、Java、Python 等。
Swift 包含了 C 和 OC 上所有的基础数据类型:Int、Double、Float、Bool、String,集合类型:Array、Set 和 Dictionary,除此之外,Swift 增加了 OC 中没有的高阶数据类型如元组(Tuple),元组方便我们接收或传递一组数据,或返回多个值而不必使用结构体、类等。
另外,Swift 新增了可选(Optional)类型,可选表示有值或者没有值,需要注意的是,Swift 中,不同类型的数据类型是不能够相互操作的,如 Int?和 Int 属于不同类型,又如 Int 和 Float。

Swift基础篇

常量let和变量var

常量一旦设定就不可改变,变量则值可变。常量使用let来声明,变量使用var表示。

let PI = 3.1415
var Str = "string"

Swift 可以根据赋值推断出类型,你也可以使用类型标注(type annotation)来指定类型

let PI: Float = 3.1415
var Str: String = "string"

一般来说声明常量或变量形式如下:

let/var name: type = value

输出print

你可以使用print函数输出常量和变量,输完换行。Swift 取消了旧版的println输出方法,另外,如果你想使用OC中的NSLog也是可以的。print(, separator: , terminator: )中,将 terminator 参数设为空字符""则可以不进行换行。
Swift使用字符串插值(string interpolation)的方式将常量或变量当作占位符加入到长字符串中,我们可以借此拼接长字符。

let value1 = "123"
var value2 = 345
print("value1 = \(value1) , value2 = \(value2)")
// 输出: value1 = 123 , value2 = 345

数据类型(布尔值、数组、字典、元组、可选类型)

  • 布尔值

和 OC 不同,Swift 中的布尔值使用truefalse表示真、假

let boolValue = true
if boolValue {
   
   
    print("value is true")
}

这里需要说明的是,Swift 不能像 OC 中那样,数值为0即表示布尔值NO,非0即为YES,因此下面OC代码可以通过

float value = 0.001;
if (value) {
   
   
	NSLog(@"value:%f",value);
}
// 输出: value:0.001000

而在Swift中会编译报错

let value = 0.001
if boolValue {
   
   
    print("value is true")
}

Swift 在判断语句中必须采用布尔值

let value1 = 1.1
let value2 = 2.0
if value1 > value2 {
   
   
    print("value1:\(value1) 大于 value2:\(value2)")
}else{
   
   
    print("value1:\(value1) 小于 value2:\(value2)")
}
// 输出: value1:1.1 小于 value2:2.0
  • 数组(array)

数组是一种泛型应用,关于泛型,后面会说到。

数组有多种创建方式

var list1:Array = [1,2,3]  // 标准创建,元素类型由系统推断
var list2:Array<Int> = [1,2,3]  // 指定类型,一种泛型应用
var list3 = [1,2,3]     // 由系统推断类型
var list4:[Int] = [1,2,3]   // 简写Array,并指定元素
var list5 = [Int]([1,2,3])  // [Int]()则创建空数组
var list6 = Array([1,2,3]) // 等同于Array.init([1,2,3])
var list7 = Array.init(repeating: 1, count: 3)  // 重复3次元素1,数组类型由元素推断

需要说明的是:Swift 中并没有可变不可变两种数组类型之分,不可变类型可以通过let来声明,var声明的数组都是可变的,类似的,字典类型也是如此。

数组的一些常用操作

var a = [Float]()
a.append(1.1) // 添加元素
a += [2,5]	// 拼接其他数组
a[1...2] = [11,22] // 修改元素, '...'表示范围range
a.insert(33, at: a.count) // 插入元素,此处插在了数组的末尾
a.remove(at: a.count-1) // 移除指定位置的,此处相当于 a.removeLast()
a.removeFirst() // 移除第一个
// 遍历数组
for value in a {
   
   
    print("index=\(a.index(of: value)!),element=\(value)")
}
for (index,value) in a.enumerated() {
   
   	// (,)表示元组类型
    print("index=\(index),element=\(value)")
}
  • 字典(Dictionary)

字典的创建和数组类似,可以由系统推断出元素类型,也可以指定元素类型。和 OC 不同的是,Swift 创建不使用 @{} 语法糖创建,而是使用和数组一样的 [] 形式,和数组不同的是,[] 中需要 : 分隔,左边是 key 的类型,右边是 value 类型,下面演示其中一种创建方式。

// [Int:String]为简写,其中Int为key的类型,String为value类型
var d:[Int:String] = [200:"success",404:"not found"]

字典的一些常用操作

d[200] // 取值
d[500] = "internal server error" // 添加
// 遍历字典
for key in d.keys {
   
     // 遍历key
    print("key=\(key)")
}
for value in d.values {
   
     // 遍历value
    print("value=\(value)")
}
for (key,value) in d {
   
      // 遍历元组
    print("key=\(key),value=\(value)")
}
  • 元组

元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,这一点和数组不同,它不要求元素相同类型,这里的元组和 Python 中的元组功能类似。
Swift 的元组使用圆括号()创建,不能省略,这点和Python不同。

let size = (100,200)
// 输出 100 200
print(size.0,size.1)
// 也可以指定名称
let httpError = (code:404,reason:"Not Found")
print(httpError.code,httpError.reason)  // httpError.code 和 httpError.0 是一样的
// 输出 404 Not Found

应用举例:实现交换

var (v1,v2) = (10,20)
print("v1:\(v1),v2:\(v2)")
// 输出 v1:10,v2:20
(v1,v2) = (v2,v1)
print("v1:\(v1),v2:\(v2)")
// 输出 v1:20,v2:10

应用举例:函数多参数返回,在数组和字典中的遍历过程中已经有体现

需要注意的是:元组在临时组织值的时候很有用,但是并不适合创建复杂的数据结构。如果你的数据结构并不是临时使用,请使用类或者结构体而不是元组。

  • 可选类型

可选类型:有值 或者 没有值

我们先来看下系统方法产出的可选类型。Int 类型有一种构造器,将一个 String 值转换为一个 Int 值,但是,并不是所有的字符串都可以转换为一个整数。如 “123” 可以被转换为数字123,而字符串"hello, world"则不可以。

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// 当你敲convertedNumber时,可以看到类型"Int?"类型 

这时因为这个构造器可能会失败,所以它返回一个可选类型 Int?,而不是 Int(注意,这两种属于不同类型),? 表示包含的值可能有值,也可能没有值,当我们需要使用 Int 类型时,需要转换为 Int 才可以。

声明一个可选类型

使用 ? 来声明一个可选类型

var serverResponseCode: Int? = 404
// serverResponseCode 包含一个可选的 Int 值 404
print("\(serverResponseCode!)")  // 这里的'!'表示强解
// 输出 404
serverResponseCode = nil
// serverResponseCode 现在不包含值

如果你声明一个可选常量或者变量却没有赋值,他们会自动被设置为nil,就像下面这样:

var surveyAnswer: String?
// surveyAnswer 被自动设置为 nil

这时如果你想要使用 ! 强解 surveyAnswer 会发生错误,所以在确定一个可选值时,务必保证可选值不为 nil。

可选绑定

我们可以使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含值就把值赋值给一个临时常量或者变量。通常会出现在 if 和 while 语句中,这条语句不仅可以用来判断可选类型中是否有值,同时可以将可选类型中的值赋值给一个常量或者变量。

let possibleNumber = "123"
if let actualNumber = Int(possibleNumber) {
   
   
    print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
} else {
   
   
    print("\'\(possibleNumber)\' could not be converted to an integer")
}
// 输出 "'123' has an integer value of 123"

因为 Int(possibleNumber) 包含来一个值,因此进入 if 的方法体,并会将该值赋值给常量 actualNumber。

隐式解析可选类型

可选类型除了使用可选绑定来解析值,我们也可以使用 ! 来解析值,但是可选此时不可出现 nil 的情况,否则会出现错误。

有时候,第一次被赋值之后,可以确定一个可选类型总会有值。在这种情况下,每次都要判断和解析可选值是非常低效的,因为可以确定它总会有值。

这种类型的可选状态被定义为隐式解析可选类型,我们可以将 ? 换成 ! 来声明一个隐式解析可选类型,这种情况下,我们无需手动使用 ! 来解析可选值,系统会帮我们完整可选类型的解析。

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值

// 表示该成员变量必定有值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString  // 不需要感叹号

需要注意的是:和普通可选类型一样,如果你在隐式解析可选类型没有值的时候尝试取值,同样会触发运行时错误。因此,如果一个变量之后可能变为 nil 的话,请不要使用隐式解析可选类型,否则请使用普通可选类型。

可选类型在开发过程中经常出现,大家需要好好理解,正确使用使用 ?!

  • Any、AnyObject和AnyClass

Any : 表示任意类型,Any? 还包括了 nil
AnyObject : 代表任务 class 类型,无论 class 是否谁的子类又或者是基类
AnyClass : AnyObject的别名,和AnyObject一样

几种运算符

这里提及一些和 OC 中不一样的几种运算符。

  • 区间运算符

区间运算符通常用于整型或者范围。区间运算符有两种: ..<... ,前者为开区间 CountableRange,后者为闭合区间 ClosedRange。

// 闭区间运算符
for i in 0...9 {
   
   
    print(i)
}
// 半开区间运算符
let names = ["Anna", "Alex", "Brian", "Jack"]
for i in 0..<names.count {
   
   
    print("第 \(i + 1) 个人叫 \(names[i])")
}
// 第 1 个人叫 Anna
// 第 2 个人叫 Alex
// 第 3 个人叫 Brian
// 第 4 个人叫 Jack

// 单侧区间
for name in names[2...] {
   
   
    print(name)
}
// Brian
// Jack
for name in names[...2] {
   
   
    print(name)
}
// Anna
// Alex
// Brian

let range = ...5
range.contains(7)   // false
range.contains(-1)  // true
  • 空合运算符

空合运算符(a ?? b)将对可选类型 a 进行判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b。注:表达式 a 必须是可选类型,而 b 的类型必须和 a 存储值的类型保持一致。

let bValue = "default value"	// 作为默认值
var aValue : String?  // 默认值为 nil
let result = aValue ?? bValue
print(result)
// 输出 default value
  • 溢出运算符

在默认情况下,当向一个整数赋值超过它的容量时,Swift 会报错。我们可以使用 maxmin 访问整形的最大或最小值

var maxInt = Int8.max
var minInt = Int8.min
print("Int8.max:\(maxInt), Int8.min:\(minInt)")
// 输出 Int.max:127, Int.min:-128

如果此时我们尝试给 maxInt 加1或者给 minInt 减1都会错误

maxInt += 1		// 编译错误
minInt -= 1		// 编译错误

我们可以使用溢出运算符来解决这种上溢或下溢现象

minInt = minInt &- 1	// 相同的有 &- &* &/
print("\(minInt)")
// 输出 127

Int8 型整数能容纳的最小值是 -128,以二进制表示即 10000000。当使用溢出减法运算符对其进行减 1 运算时,符号位被翻转,得到二进制数值 01111111,也就是十进制数值的 127,这个值也是 Int8 型整数所能容纳的最大值。

另外,在新版 Swift 中,++-- 运算符被取消,因此 i++ 这种形式的累加需要换成 i += 1 这种形式。

控制语句

Swift 提供了多种控制流语句,包括 while 循环、if、guard、switch、跳转break、continue 等等。在 Swift 中,for-in 循环可以更简单的遍历数组、字段、区间、字符等序列类型,不同于 C 和 OC,Swift 在新版本中取消了 C 的 for 条件循环,即 for var i=0;i<a;i++ 这种形式。值得一提的是,Swift中的 switch 语句比 C 中更加强大,case 可以匹配不同的模式,包括范围匹配,元组和特定类型匹配,甚至是使用 where 来描述更多约束的情况。详情可以看官网翻译的控制流。

我们这里介绍提前退出 guard

像 if 语句一样,guard 的执行取决于一个表达式的布尔值。当 guard 要求条件的为真时,可以继续执行 guard 语句之后的代码,否则提前返回结束。和 if 不同的是,guard通常只有一个 else 从句。

func greet(_ name:String) {
   
   
    guard name == "LOLITA0164" else {
   
   
        print("hello! \(name)")
        return
    }
    print("\(name)")
}
greet("xiao ming")	// 输出 hello! xiao ming
greet("LOLITA0164")	// 输出 LOLITA0164

我们看到,当输入"xiao ming" 时,条件为 false,输出 “hello! xiao ming” 后返回退出,当输入 “LOLITA0164” 时,条件为真,继续执行 guard 后面当语句,输出 “LOLITA0164”。

因此 guard 可以用来控制语句不满足条件时提前结束。

函数和闭包

  • 函数

Swift 的函数参数和返回值非常灵活。参数可以是的无参、多参、默认参、可变参、甚至一些高级语言中的输入输出参数,参数类型也非常多,甚至包括另一个函数,另外Swift 函数支持嵌套参数。在返回类型上,可以是无返回类型,多参数返回、甚至是一个函数。

Swift 的函数使用 func 声明一个函数,形式为:

func name(parameters) -> return type {
   
   
    function body
}

在上一个介绍中,已经涉及到了函数创建和使用 greet,下面介绍一下常用的函数形式。

// 无参数函数,有返回值
func sayHello() -> String {
   
   
    return "hello, world"
}
// 多参数函数,无返回值( -> Void 可以省略 )
func personInfo(name: String, age: Int) -> Void {
   
   
    // function body
}
// 多重返回值,这里返回一个元组
func minMax(array: [Int]) -> (min: Int, max: Int) {
   
   
    return (array.min()!, array.max()!)
}
// 可选返回类型,‘_’ 表示忽略参数标签
func max(_ array: [Int]) -> Int? {
   
   
    return array.max()
}
// 指定参数标签(参数说明,和参数名以空格分隔),区别于参数名,默认参数名就是外部的标签名
func someFunction
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值