前言
很庆幸有golang 这样的语言面世。语义清晰的同时概念又特别简洁,去掉了很多无谓传统编程语言繁杂又无效率的写法,作者们通过了实现表达了我们这些广大程序员敢想不敢言的心声。
我也算是在IOS领域上摸爬滚打接近十年的老人了。要说Object-C 语法思维属于反人类的绝对错不了,但只要掌握了它的消息机制就会觉得不花巧又实用。 没想到作为后继者的Swift,反人类的写法一点都不逊色于前辈,同时要说起语法卖弄,它敢称第二绝对不会有其他语言敢占这排名。
以下是我从Object-C 转型到Swift 后的初期最需要注意的功能点:
-对变量常量的定义:let 和var,可选项? 、!
- 控制流
- 函数和闭包
- 结构体和类
- 下标脚本
- 可选链
- 扩展
- 泛型
Swift对变量常量的定义:let 和var,可选项? !
let代表不可变对象,即对象首地址不能发生变更,也就是说内存地址不能改变,但也可以修饰可变对象。相当于其它语言的const 或者#define 表达。与传统语言比较,Swift 不允许直接对变量、常量定义为nil,也不允许执行过程中赋值nil。
let someInt: Int = 100
someInt = 50 // 报错误 ,因为let修饰的变量不能改变指针指向
//let 可以允许定义时不赋值,第一次使用再赋值
let otherInt: Int
otherInt = 80 // 不会报错。
let nilStr: String = nil //不允许设置为nil,报错:cannot initialize specified type 'String'
var代表可变对象,即内存地址可以发生改变,可以修饰指针重新指向的变量。
var someStr: String = "Hello "
someStr = "Hello world" // 不会报错,因为var修饰的变量指针可以重新指向
someStr = nil //不允许设置为nil,报错:cannot initialize specified type 'String'
为了允许变量支持空值赋值,在定义变量时增加可选项: ? !
var some2Str: String? = nil;
var other2Str: String! = nil;
在Swift5 后,不论是添加! 还是添加?变量的定义都等价于
//类型已经从String 转变为Optional<String>
var some2String: Optional<String>
var other2String: Optional<String>
而变量访问的方式也相应产生变化:
print(some2String ?? "this is default value")
/*
添加?? 等价于以下写法:
if some2String == nil{
print("this is default value")
}
else{
print(some2String)
}
*/
print(some2String!)
/*
添加! 对变量进行访问的写法等价于:
if some2String == nil{
throw ('Fatal error: Unexpectedly found nil while unwrapping an Optional value')
}
else{
print(some2String)
}
*/
同时访问的方式也可以变成另一种写法:
if let defString = some2String {
print(defString)
}
//把some2String 进行展开,把真实的类型赋值到defString
Swift的控制流
与传统语言对比,Swift 中的 if 和 switch 作为条件控制变化不大。而 作为循环体的 for-in 、 while 、 repeat-while 是需要注意的。其中for-in 循环的写法为:
let scores = [34, 21, 3, 99, 101]
//写法一
for score in scores {
//语句执行
}
//写法二
for (key,item) in scores.enumerated(){
//key 为当前数组枚举的索引值
//item 为值
}
//写法三 :通过 ".." 来表示索引范围或明确声明一个初始值、条件、增量
for index in 0...scores.count{
//语句执行
}
//写法四:通过..< 来表示索引范围到scores.count-1 后结束。
for index in 0..<scores.count {
//语句执行
}
while 循环可以有两种写法:
/*
while condition
{
statement(s)
}
*/
while index <100{
print(index)
}
/*
repeat
{
statement(s);
}while( condition );
*/
var index = 0
repeat{
print( "index 的值为 \(index)")
index = index + 1
}while index < 20
Swift的函数和闭包
Swift 函数语法定义:
/*
func funcname(外部形参名 内部形参名 类型, ...) -> returntype {
Statement1
Statement2
……
Statement N
return parameters
}
*/
//其中内部形参名非必填
//示例一:只有外部形参
func runoob(name: String, site: String) -> String {
return name + site
}
funoob(name:"Hello",site:"world")
//示例二:外部形参 +内部形参
func runoob(name _name: String, site _site: String) -> String {
return _name + _site
}
funoob(name:"Hello",site:"world")
//示例三:匿名外部形参
func runoob(_ _name: String, _ _site: String) -> String {
return _name + _site
}
funoob("Hello","world")
//示例四:可变形参
func vari<N>(members: N...){
for i in members {
print(i)
}
}
vari(members: 4,3,5)
vari(members: 4.5, 3.1, 5.6)
vari(members: "Hello", " ", "World")
//示例五:可通过函数内变更参数值 的地址引用 可选项:inout
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
a = a ^ b
b = b ^ a
a = a ^ b
}
var x = 1
var y = 5
swapTwoInts(&x, &y)
print("x 现在的值 \(x), y 现在的值 \(y)")
//示例六:返回类型可以是一个元组
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
print("最小值为 \(bounds.min),最大值为 \(bounds.max)")
}
闭包
就是把函数该简化的省略,该匿名则匿名,去其糟粕留其精华。自己写好了就是美如画,写烂了接手方如同恶梦。
以下定义了一个接收参数并返回指定类型的闭包语法:
/*
{(parameters) -> return type in
statements
return parameters
}
*/
//简单的示例:
let closure = {(val1: Int, val2: Int) -> Int in
return val1 / val2
}
let result = closure(100, 50)
print (result)
闭包常用在函数回调传参的写法上,以标准库sorted方法为示例来做一个闭包逐步简化的演示:
//在不使用闭包的写法上,可以使用定义传统函数的方式:
let names = ["AT", "AE", "D", "S", "BE"]
// 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
func backwards(s1: String, s2: String) -> Bool {
return s1 > s2
}
var reversed = names.sorted(by: backwards)
print(reversed)
//利用闭包简写后,从关键字in 后是具体的执行命令
var reversed = names.sorted(by:{(s1:String, s2: String) ->Bool in
return s1 > s2
})
print(reversed)
//上述写法还是复杂,再简约一点,参数名、返回结构都不要了
//$0、$1 .....$n 去表示参数的顺序
var reversed = names.sorted(by:{
return $0 > $1
})
print(reversed);
//甚至在主函数只有一个函数形参时,可以去掉形参以及return,把代码书写到外面。
var reversed = names.sorted(){
$0 > $1
}
print(reversed);
//这样够了吗? 不,还可以写得更“简约”:
var reversed = names.sorted(by:>)
Swift的结构体和类
结构体的关键词是struct ,可以拥有自己的构造函数,没有释构函数。格式如下:
/*
struct nameStruct {
Definition 1
Definition 2
……
Definition N
init(parameters){
}
}
*/
var aStruct = MarksStruct(mark: 100)
var bStruct = aStruct // aStruct 和 bStruct 是使用相同值的结构体!
bStruct.mark = 20
print(aStruct.mark) // 100
print(bStruct.mark) // 20
类(Class),相比起结构体,有以下不同点:
- 继承允许一个类继承另一个类的特征
- 类型转换允许在运行时检查和解释一个类实例的类型
- 解构器允许一个类实例释放任何其所被分配的资源
- 引用计数允许对一个类的多次引用
语法:
/*
class classname {
Definition 1
Definition 2
……
Definition N
}
*/
class MarksStruct {
var mark: Int
//构造函数
init(mark: Int) {
self.mark = mark
}
//释构函数
deinit{
print("MarksStruct is deinit");
}
}
class studentMarks {
var mark = 300
}
var p_mask: MarksStruct? = MarksStruct(mark: 500)
if let let_p_mask = p_mask {
print("成绩为 \(let_p_mask.mark)")
}
p_mask = nil;
let a_marks = studentMarks()
print("成绩为 \(a_marks.mark)")
下标脚本
给结构体定义subscript 方法,显式声明入参(一个或多个)和返回类型后,则结构体可以达成类似someDictionary[key]方式的读写:
/*
subscript(index: Int) -> Int {
get {
// 用于下标脚本值的声明
}
set(newValue) {
// 执行赋值操作
}
}
*/
class daysofaweek {
private var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Tsday", "Friday", "saturday"]
subscript(index: Int) -> String {
get {
return days[index] // 声明下标脚本的值
}
set(newValue) {
self.days[index] = newValue // 执行赋值操作
}
}
}
var p = daysofaweek()
print(p[0])
print(p[1])
print(p[2])
print(p[3])
p[4] = "Thursday"
print(p[4])
以上程序执行输出结果为:
Sunday
Monday
Tuesday
Wednesday
Thursday
下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制。返回值也可以是任何类型。同时也可以使用变量参数和可变参数。
struct Matrix {
let rows: Int, columns: Int
var print: [Double]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
print = Array(repeating: 0.0, count: rows * columns)
}
subscript(row: Int, column: Int) -> Double {
get {
return print[(row * columns) + column]
}
set {
print[(row * columns) + column] = newValue
}
}
}
// 创建了一个新的 2 行 2 列的Matrix实例
var mat = Matrix(rows: 2, columns: 2)
// 通过下标脚本设置值
mat[0,0] = 3.0
mat[0,1] = 6.0
mat[1,0] = 9.0
mat[1,1] = 12.0
// 通过下标脚本获取值
print("\(mat[0,0])")
print("\(mat[0,1])")
print("\(mat[1,0])")
print("\(mat[1,1])")
可选链
回顾最初变量常量所定义的 可选项(! ?),同样适用在类和结构体上。通过在属性、方法、或下标脚本的可选值后面放一个问号(?),即可定义一个可选链。
class SubTree {
var numberOfTress = 1
}
class Root {
var subTree: SubTree? //定义属性为可选项
}
使用感叹号(!)访问可选链实例,强制检测属性是否存在,不存在则抛出异常。
let root = Root()
//将导致运行时错误
let numberOfTress = root.subTree!.numberOfTress;
//输出结果:fatal error: unexpectedly found nil while unwrapping an Optional value
/* 以上语句,等价于传统写法:
try {
let numberOfTress = root.subTree.numberOfTress
}catch{
throw ('Fatal error: Unexpectedly found nil while unwrapping an Optional value')
}
*/
使用问号(?)可选链实例,检测属性是否存在,不存在则正常返回nil值,不报异常,这处理方式非常接近Object-C 的非空检测。
// 链接可选residence?属性,如果residence存在则取回numberOfRooms的值
if let numberOfTress = root.subTree?.numberOfTress {
print("树的 节点总量有 \(numberOfTress)。")
} else {
print("不存在节点")
}
扩展
扩展就是向一个已有的类、结构体或枚举类型添加新功能。扩展可以对一个类型添加新的功能,但是不能重写已有的功能。
语法:
extension SomeType {
// 加到SomeType的新功能写到这里
}
示例:
extension Int {
var add: Int {return self + 100 }
var sub: Int { return self - 10 }
var mul: Int { return self * 10 }
var div: Int { return self / 5 }
}
let addition = 3.add
print("加法运算后的值:\(addition)")
let subtraction = 120.sub
print("减法运算后的值:\(subtraction)")
let multiplication = 39.mul
print("乘法运算后的值:\(multiplication)")
let division = 55.div
print("除法运算后的值: \(division)")
let mix = 30.add + 34.sub
print("混合运算结果:\(mix)")
泛型
Swift 提供了泛型的标准模板,来让开发者写出可重用的函数和类型。
一个简单的示例:
// 定义一个交换两个变量的函数
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var numb1 = 100
var numb2 = 200
print("交换前数据: \(numb1) 和 \(numb2)")
swapTwoValues(&numb1, &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")
var str1 = "A"
var str2 = "B"
print("交换前数据: \(str1) 和 \(str2)")
swapTwoValues(&str1, &str2)
print("交换后数据: \(str1) 和 \(str2)")
以结构体来实现一个栈示例:
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var stackOfStrings = Stack<String>()
print("字符串元素入栈: ")
stackOfStrings.push("google")
stackOfStrings.push("runoob")
print(stackOfStrings.items);
let deletetos = stackOfStrings.pop()
print("出栈元素: " + deletetos)
var stackOfInts = Stack<Int>()
print("整数元素入栈: ")
stackOfInts.push(1)
stackOfInts.push(2)
print(stackOfInts.items);
入门学习的资料参考:
Swift教程| 菜鸟教程
Swift 5 从零到精通IOS开发训练营