数据定义
语法规则
先来看下下面的代码
import Cocoa
var num1 = "four" //a
var num2: String = "four" //b
var num3 = 4 //c
var num4: Int = 4 //d
上面的几行代码都能正常运行,其中a和b行等价,c和d行等价。区另就在于是否声明了类型,虽说swift会根据值的类型来推断其变量的类型,保证程序运行不会出现,但还是建议以b和d行的写法为准,因为可读性比较强也会避免很潜在的bug。
可简单理解为swift的类型是一种兼容弱和强类型的一种语法格式,在实际使用中强烈建议采用强类型来定义;
数据类型
在Swift中数据分为常量和变量两种,分别会使用不同的关键字来修饰:
var
:用于定义变量,即值可被改变;let
:用于定义常量,即值一旦初始化后就不允许改变;
//用let定义常量,值不能被改变
let numberOfStoplights: Int = 4
let townName: String = "Londo"
//用var定义变量,值可以被改变
var population: Int = population = 5422
//采用\(varName)的方式来引用变量
let townDescription =
"\(townName) has a population of \(population) and \(numberOfStoplights) stoplights."
//值打印到Console控制台上
print(townDescription)
代码注释
有两种注释方法:
- 单行注释:
//
- 多行注释:
/* */或 /** /
//我是单行行注释,不能换行写哟
/**
如果有多行注释要写,可以用这种方式
*/
带标题的注释,用MARK:
分隔,但只能用于行注释不能用于块注释
// MARK: Extend Velocity to suport converting
// I.e., there is a problem with the interchangeability of Velocity for Double
基础数据类型
Character
Swift 的字符是一个单一的字符字符串字面量,数据类型为 Character。
import Cocoa
let char1: Character = "A"
let char2: Character = "B"
print("char1 的值为 \(char1)")
print("char2 的值为 \(char2)")
//~~char1 的值为 A
//~~char2 的值为 B
Swift 中不能创建空的 Character(字符) 类型变量或常量:
import Cocoa
// Swift 中以下赋值会报错
let char1: Character = ""
var char2: Character = ""
print("char1 的值为 \(char1)")
print("char2 的值为 \(char2)")
//~~ error: cannot convert value of type 'String' to specified type 'Character'
布尔
布尔值字面量有三个值,它们是 Swift 的保留关键字:
- true 表示真。
- false 表示假。
- nil 表示没有值。
let aBool = true //布尔值字面量
let a:Bool = true
字符串
组成Swift字符串底层实现为集合形式,由多个Character类型的单个字符组成。
声明
在swift中字符串不分可变和不可变,可变的特性全部接由var和let来限定。
//以下两种声明方式是等价的
var errorString: String = "The request failed:"
var errorString = "Hello, playground"
var errorString += "!"
//计算长度
print(playground.count)
unicode声明,标准语法格式为: \u{}。值为十六进制的数。
let oneCoolDude = "\u{1F60E}"
let aAcute = "\u{0061}\u{0301}"
遍历
//逐个字母打印
let playground = "Hello, playground"
for scalar in playground.unicodeScalars {
print("\(scalar) ")
//print("\(scalar.description) ") //与上面代码等价
//print("\(scalar.value) ")
}
索引与区间
这些方法比较特殊,需要额外记一下。
let playground = "Hello, playground"
let start = playground.startIndex
let end = playground.index(start, offsetBy: 4)
let fifthCharacter = playground[end]
let range = start...end
let firstFive = playground[range]
- .startIndex:获取字符串的第一个索引,实际值是0;
- .index(start, offsetBy: 4):取得从start往前数4个字符,即第5个字符,返回的一个索引值,实际值为5;
- []:返回索引或索引区间指定的字符或字符串,此处返回hello中的o;最后一行代码返回的是hello;
- range:表示区间,在本例中实际值为0~5;
下面是更复杂一点的区间的例子
let playground = "Hello, playground"
let start = playground.startIndex
let end = playground.index(start, offsetBy: 4)
let s1 = playground[start...] //Hello, playground
let s2 = playground[..<end] //Hell
let s3 = playground[start..<end] //Hell
整数
在Mac OS中,整形大小不固定,其会根据关键字来区分,比如在swift中整形可定义为:Int、Int8、Int16、Int32和Int64这几种,其中Int==Int64。
另外还有无符号数,UInt、UInt8、UInt16、UInt32和Int64这几种,即其值只能为正数。
var age: Int8 = 98
print("The maximum Int value is \(Int.max)")
print("The minimum Int value is \(Int.min)")
print("The maximum value for a 32-bit integer is \(Int32.max).")
print("The minimum value for a 32-bit integer is \(Int32.min).")
print("The maximum UInt value is \(UInt.max).")
print("The minimum UInt value is \(UInt.min).")
/*
The maximum Int value is 9223372036854775807
The minimum Int value is -9223372036854775808
The maximum value for a 32-bit integer is 2147483647.
The minimum value for a 32-bit integer is -2147483648.
The maximum UInt value is 18446744073709551615.
The minimum UInt value is 0.
*/
溢出操作
在swift中,整数都有一个范围,默认时就超出范围时就会报错,比如Int8最大值是-128~127之间,那么下面的代码就会有问题:
var overflow: Int8 = 120
overflow = overflow + 50
由于溢出中断了,如果想保证上面的程序能正常运行,则可以使用&关键字
var overflow: Int8 = 120
overflow = overflow &+ 8 //-128
注意这种溢出的值是从最小值折返着算,即Int8最大值为127,所以128会从最小值开始算为-128, 129则为-127。这一点比较有争议,但其实很多语言全是这样处理的。
这种溢出操作如无需要尽量不要使用,把所有的整数全定义为Int即可。
类型转换
在swift中不存在自动升级的情况,需要手工转换,比如下面的代码会出问题
var i16: Int16 = 32
var i8: Int8 = 2
var sum: Int16 = i16 + i8
可做如下修正
var i16: Int16 = 32
var i8: Int8 = 2
//只能类型相同
var sum: Int16 = i16 + Int16(i8)
//下面这行也会报错
var sum: Int32 = i16 + Int16(i8)
浮点数
在swift中有两种浮点数:Float表示32位,Double表示64位,区别在于精度高低。但同整数一样也会有Float32这样的类型。
let d2: Double = 1.1
let f1: Float = 100.3
print(10.0 + 11.4) //21.4
print(11.0 / 3.0) //3.6666666666666665,默认16位小数位
var d3: Double = d2 + Double(f1) //101.4000030517578
关于浮点数据的特殊说明:
- 浮点数天生就是不精确的,所以不能用于科学计算;
- 浮点数不能用==来比较;
- 也会存在类型转换的问题;
精准度测试
let d11 = 1.1
let d22: Double = 1.1
if d11 == d22 { //TRUE,打印
print("d1 and d2 are the same!")
}
print("d11 + 0.1 is \(d11 + 0.1)") //1.2000000000000002
if d11 + 0.1 == 1.2 { //FALSE,不会打印
print("d1 plus 0.1 is equal to 1.2")
}
可空类型
可空类型(optional)是Swift的独特特性,用来指定某个实例可能没有值,如果一个实例没有值,也可称其为nil。任何类型都可以用可空类型来说明一个实例可能是nil,这和java的null有点类似。
本节主要讲述如何声明可空类型,如何使用可空实例绑定(optional binding)来检查某个可空实例是否为nil并且在有值的情况下使用其值,以及如何使用可空链式调用(optional chaining)来查询一连串可空值。
Swift为何要设计可空类型?Swift 设计可空类型的原因是为了处理缺失的值。在没有指定某个值可能为空的情况下,尝试访问这个值会导致运行时错误。通过使用可空类型(Optional),Swift 提供了一种安全的方式来处理可能缺失的值。可空类型在 Swift 中表现为一个抽象的类型 Optional,它有两个主要的类型 Optional.Some 和 Optional.None。当你声明一个变量为可空类型时,它可能包含一个具体的值,也可能为空(nil)。这样可以避免空指针异常等常见的运行时错误。确保了在编译时就能捕获潜在的空值问题,而不是等到运行时才崩溃。这有助于写出更加健壮和安全的代码。
例如,以下是一个可空类型的声明和使用
不可自动展开声明,关键字 ?
语法格式:var/let varName: varType? //关键字 ?
var errorCodeString: String? //这行的值为nil
errorCodeString = "hello"
print("\(errorCodeString)") //~~ Optional("404")
上面打印可空字符串时会在值外面套一个Optional("),现在可以用!来恢复正常输出,术语称为强制展开,强制展开会访问空实例封装的值。
var errorCodeString: String?
errorCodeString = "404"
print("\(errorCodeString)") //Optional("404")
if(errorCodeString != nil){
print(errorCodeString!) //~~ 404
}
但需要注意,要强制展开一个空实例时,程序会出错异常,这也是上在的代码为何要加一个if的原因,比如下面这行代码就会报:Fatal error: Unexpectedly found nil while unwrapping an Optional value 异常。
var errorCodeString: String?
print("\(errorCodeString!)")
可自动展开声明,关键字 !
功能同用?声明的差不多,唯一的区别就是在展开时不需要写if判断语句了,其实的特性基本一样。
- 在有值时,与
?
作用相同
var errorCodeString: String!
print(errorCodeString) //输出nil
errorCodeString = "404"
print(errorCodeString) //输出Optional("404")
print(errorCodeString!) //输出404
//下面这两行代码,在errorCodeString不等于nil时作用相同
let anotherErrorCodeString: String = errorCodeString! //404
let yetAnotherErrorCodeString = errorCodeString //404
- 在值为
nil
时,则有一种写法就可以带来便利
var errorCodeString1: String!
let anotherErrorCodeString3 = errorCodeString1 //nil,正常
let anotherErrorCodeString4 = errorCodeString1!//异常
let anotherErrorCodeString1: String = errorCodeString1! //异常
let anotherErrorCodeString2: String = errorCodeString1 //异常
可空实例绑定
可空绑定,用于判断空实例用,如果非nil就将其赋给一个临时常量或变量,并且使这个常量或变量在条件语句的第一个分支代码中可用。
var errorCodeString: String?
//因为errorCodeString=nil所以此处不成立,不走print打印
if let theError = errorCodeString {
print("\(theError): \(errorCodeString)")
}
//这段代码和上面的代码等价
//if nil = errorCodeString {
// let theError =errorCodeString
// print("\(theError): \(theError)")
//}
//给theError赋值404,然后再进行运算,此处代码成立会打印print
errorCodeString = "404"
if let theError = errorCodeString {
if let errorCodeInteger = Int(theError) {
print("\(theError): \(errorCodeInteger)")
}
}
上面这种多个if的写法会比较麻烦,可以简写成以下的样子
var errorDescription: String?
if let theError = errorCodeString, let errorCodeInteger = Int(theError){
print("\(theError): \(errorCodeInteger)")
}
可空链式调用
可空链式调用提供了一种对可空实例进行查询以判断其是否包含值的机制。与上面讲到的绑定相比,可空链式调用允许把多个查询串联为一个可空实例的值。即如果链式调用中的每个可空实例都包含值,那么每个调用都会成功,如果查询链中有一个可空实例为nil,那么整个链式调用就会返回nil。
看着挺复杂,其实就是方法调用
var errorCodeString: String!
errorCodeString = "404"
var errorDescription: String?
if let theError = errorCodeString, let errorCodeInteger = Int(theError), errorCodeInteger == 404 {
errorDescription = "\(errorCodeInteger + 200): resource was not found."
}
//这处用.调用就是可空链式调用
var upCaseErrorDescription = errorDescription?.uppercased()
print(upCaseErrorDescription) //~~ Optional("604: RESOURCE WAS NOT FOUND.")
可空类型的值修改
术语称为原地修改,目的是避免创建新变量的麻烦,这种修改方式不受nil值的限制(仅限于?),在上面的示例代码中再添加几何观察下输出:
upCaseErrorDescription?.append(" PLEASE TRY AGAIN.")
//~~ Optional("604: RESOURCE WAS NOT FOUND. PLEASE TRY AGAIN.")
print(upCaseErrorDescription)
errorDescription = nil
let description = errorDescription ?? "No error"
//~~ No error
print(description)
可空类型运算
用??来实现便捷操作,相当于三元运算符,但??写法只能用于可空类型
var errorDescription: String?
let description = errorDescription ?? "No error" //No error
errorDescription = "500"
description = errorDescription ?? "No error" //500
//隐式展开可空类型,使用时一定不能为空,不然会报错
var test:String!
let aa:String? = nil //nil
var bb = aa ?? "hha" //hha
使用可空类型有几点需要注意的点:
- ?号表示可空类型,等价于var errorCodeString: String? = nil,但不等价于var errorCodeString: String = nil,因为可空类型会有很多新特征可以使用;
- 在?和!的取舍上尽量用?,只有在知道可空实例一定不会为nil或一旦为nil程序就会退出时才可以使用!。
print打印函数
标准结构如下所示:
print("The request failed")
var errorString: String = "The request failed"
print(errorString)
占位符
在拼装字符串时,可用占位符模式,语法格式:“\(varName) ”
。
let numberOfStoplights: Int = 4
var population: Int = 5422
let townName: String = "Londo"
let townDescription = "\(townName) has a population of \(population) and \(numberOfStoplights) stoplights."
//~~Londo has a population of 5422 and 4 stoplights.
print(townDescription)