Swift学习笔记之类型、常量和变量

Swift学习笔记

根据《Swift编程权威指南》和 Swift官方文档 学习整理的Swift学习笔记,持续更新,中间有什么错误,欢迎大家留言讨论。

系列总目录

官方文档

swift官方文档

一、类型、常量和变量

1、变量

​ 使用 var关键字声明个变量。

var num = 4
num += 2

2、常量

​ 使用 let 关键字声明一个常量,常量只能有一次赋值。

let numL = 10

3、类型推断

​ 可以在变量/常量名后使用“ : 类型 ”,定义类型。

let cityName: String = "潍坊"

4、数

4.1 整数

4.1.1 Int、UInt、Int32 的最大值与最小值

​ 如果需要知道整数精确长度,可以使用Swift的显式长度整数类型,比如Int32是Swift的32位有符号整型。对于8位、16位和64位有符号整型,相应的还有Int8Int16Int64

​ 有符号整型和无符号整型的最小值和最大值之间有对应关系:UInt64的最大值是Int64的最大值与最小值绝对值之和。无论是有符号整型还是无符号整型,都有2的64次方种可能的值,但是有符号整型需要把一半可能的值留给负数用。

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).")
print("The maximum value for a 32-bit unsigned integer is \(UInt32.max).")
print("The minimum value for a 32-bit unsigned integer is \(UInt32.min).")

​ 执行结果如下图所示:
执行效果

4.1.2 创建整数实例

显式和隐式地声明Int

let numA:Int = 10
let numB = 3

显式声明其它整数类型

let numUInt: UInt = 40
let numInt32: Int32 = -1000
4.1.3 整数操作符
  • 基本操作符:加(+)、减法(-)、乘(*)、除(/
print(50+30)
print(50-30)
print(50*30)
print(60/30)
  • 操作符运算顺序,使用圆括号
print(10 + 2 * 5) // 20,因为 2 * 5  先计算
print(30 - 5 - 5) // 20,因为 30 - 5 先计算

print((10 + 2) * 5) // 60,因为(10 + 2)先计算
print(30 - (5 - 5)) // 30,因为(5 - 5)先计算
  • 整数除法
print(11 / 3) // 打印3

​ Swift把小数部分截断后留下了3。整数除法总是向0舍入。

  • 取余操作符
print(11 % 3) // 打印2
print(-11 % 3) // 打印-2

​ 需要注意的是,取余运算符不ܹ太一样,用在负整数上的结果可能会出乎你的意料。

  • 快捷操作符

    +=-+*=/=%=,每个都会把运算结果赋给操作符左边的值。

var x = 10
x += 10 // 等同于: x = x + 10
x -= 5 // 等同于: x = x - 5

注意:从swift3开始,去除了自增(++)和自减(–)操作符

  • 溢出操作符

先看以下代码:

let y: Int8 = 120
let z = y + 10

Xcode报错,“执行中断”,是什么意思?下面来分解每一步发生了什么:

(1) y是Int8类型,所以编译器认为y + 10也必须是Int8,因而推断z是Int8;

(2) 运行后,Swift给y加10,得到130;

(3) 在把结果存入z 之前,Swift检查发现130对于Int8来说是非法值。

​ 因此程序会触发陷阱,程序停止运行。Swift提供溢出操作符(overflow operator),它们在值ܹ太大(或ܹ太小)时的行为不同于普通操作作符,会绕过去而不是处罚陷阱。

​ 下面是加法的溢出操作符&+实例代码。

let y: Int8 = 120
let z = y + 10
let z = y &+ 10
print("120 &+ 10 is \(z)")

​ 执行上述代码,将得到 -126。

​ 因为y是Int8类型的, 所以一旦达到到127就不能再往上加了。在加1之后不会得到128,而是折回到了-128。因此120 + 8 = -128,120 + 9 = -127,120 + 10 = -126。

​ 减法和乘法也有ຼ溢出版本的操作符:&-&*。减法很明显不会上溢,但是可能会下溢(underflow)。比如说,一个值为-120的Int8减去10会让结果ܹ太小,无法用Int8表示。用&-会让下溢折回到正数,得到126。

4.1.4 转换整数类型

​ swift并不支持类型的自动转换,需要手动转换类型使之匹配。

let a: Int16 = 200
let b: Int8 = 50
let c = a + b 			// 报错

let c = a + Int16(b)	// 正确

4.2 浮点数

​ Swift有两种基本的浮点数类型:32位数Float和64位数Double。Float和Double 的长度差异并不像整数那样影响其最小值和最大值,而是影响其精度。Double的精度比Float高,这意味着它能存储更精确的近似值。在swift中,浮点数的默认类型推断是Double。

​ 浮点数通常不准确,有很多数无法以浮点数的形式精确存储。计算机会存储一个近似值,非常接近你要的数。

4.2.1 声明浮点数
let d1 = 1.1 			// 隐式Double声明
let d2: Double = 1.1	// 显式Double声明
let f1: Float = 100.3	// 显式Float声明
4.2.2 浮点数的操作与比较上的坑
print(10.0 + 11.4)
print(11.0 / 3.0) 

if d1 == d2 {
    print("d1 and d2 are the same!")
}

print("d1 + 0.1 is \(d1 + 0.1)")
if d1 + 0.1 == 1.2 {
    print("d1 + 0.1 is equal to 1.2")
}

​ 执行上述代码,会发现,d1 + 0.1的结果是1.2,但是第二个在if语句中的print()函数却没有运行。正如之前所说的那样,很多数(包括1.2)无法用浮点数精确表示。计算机会存储一个非常接近1.2的近似值,当你给1.1加0.1后,结果实际上是类似于1.200 000 000 000 000 1的值,而你输入字面量1.2后存储的值近似于1.199 999 999 999 999 9。尽管在打印的时候Swift会把两者都舍入为 1.2,但实际上说它们并不相等。

4.3 浮点数和整数的转换

​ 整数和浮点数字类型之间的转换必须明确。

let three = 3
let pointOneFourOneFiveNine = 0.14159
let pi = Double(three) + pointOneFourOneFiveNine
// pi equals 3.14159, and is inferred to be of type Double

​ 浮点数到整数的转换也必须明确。整数类型可以使用DoubleFloat值初始化。

注意:以这种方式初始化新的整数值时,浮点值始终会被截断。

let integerPi = Int(pi)
// integerPi equals 3, and is inferred to be of type Int

4.4 数字文字

  • 定义其他进制整数

十进制数,无前缀

二进制数,用0b前缀

八进制数,用0o前缀

十六进制数,用0x前缀

let decimalInteger = 17			// 十进制
let binaryInteger = 0b10001     // 二进制
let octalInteger = 0o21         // 八进制
let hexadecimalInteger = 0x11  	// 十六进制
  • 浮点数

​ 浮点文字可以是十进制(不带前缀)或十六进制(带0x前缀)。它们的小数点两侧必须始终有一个数字(或十六进制数字)。小数浮点数也可以有一个可选的指数,用大写或小写表示e; 十六进制浮点数必须具有指数,以大写或小写表示p

let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let hexadecimalDouble = 0xC.3p0
  • 数字文字可以包含额外的格式
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1

5、类型别名

类型别名为现有类型定义备用名称。使用typealias关键字定义类型别名。

typealias AudioSample = UInt16

​ 定义类型别名后,可以在任何可能使用原始名称的地方使用别名:

var a = AudioSample.min
var b :AudioSample = 2

6、布尔值

​ Swift具有一个基本的布尔类型,称为Bool。布尔值被称为逻辑值,因为它们只能是true或false。Swift提供了两个布尔常量值,true以及false

let b1 = true
let b2 = false

7、元组

详见 二、流程控制、2.7

8、字符串和字符

​ Swift的StringCharacter类型提供了一种快速,符合Unicode的方式来处理代码中的文本。

8.1 使用字符串

8.1.1 声明字符串
let letString = "Hello, playground"	// 常量字符串
var varString = "Hello, mutable playground"	// 可变字符串
varString += "!"				// 给可变字符串添加内容
let quotation = """				// 多行字符串
    这是一个多行
    字符串
"""

转义的特殊字符\0(空字符),\\(反斜杠),\t(水平制表符),\n(换行符),\r(回车),\"(双引号)和\'(单引号)

8.1.2 使用Characters

​ 组成Swift字符串的字符都是Character类型。Swift的Character类型表示Unicode字符,组合起来形成String实例。

for c: Character in mutablePlayground.characters {
 	print(c)
}
8.1.3 连接字符串和字符

String值可以与加法运算符(+)或者快捷运算符(+=)以创建新String值:

let string1 = "hello"
let string2 = " swift"
var welcome = string1 + string2		// "hello swift"

var instruction = "apple"
instruction += string2		//  "apple swift"

​ 可以使用append()方法连接字符串。

let exclamationMark: Character = "!"
welcome.append(exclamationMark)		// 	"hello swift!"

不能将StringCharacter加到现有Character变量,因为一个Character值只能包含一个字符。

8.1.4 拼接字符串

​ 使用 \() 在字符串中拼接

let numL = 10
let cityName: String = "潍坊"
let cityDes = "\(cityName)是世界风筝之都,\(numL)"

8.2 Unicode

​ Unicode是字符编码的国际标准,目标是不用考虑其他平台即可无缝处理和表达字符。

8.2.1 Unicode标量

​ 从内部实现来说,Swift字符串是由Unicode标量(Unicode scalar)组成的。Unicode标量是21位的数,表示Unicode标准中一个特定字符。

  • 使用Unicode标量
let oneCoolDude = "\u{1F60E}"

\u{}语法表示Unicode标量,十六进制数放在花括号中。在本例中,oneCoolDude被置为“戴墨镜”emoji的字符。

  • 使用组合标量
let aAcute = "\u{0061}\u{0301}"		// 使用组合标量  结果:á

​ Swift中的每个字符都是扩展字形簇(extended grapheme cluster)。扩展字形簇是人类可读的单个字符,由一个或多个Unicode标量组合而成。

  • 显示字符串背后的Unicode标量
let playground = "Hello, playground"
for scalar in playground.unicodeScalars {
    print(scalar)
}

​ 执行上述代码,输出:72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100

​ 控制台中的每个数对应一个字符串标量,表示字符串中的单个字符。但是这些数不是十六进制的 Unicode数,而是用无符号32位整数表示的。

8.2.2 标准等价

​ 组合标量有其存在意义,不过Unicode也为某些常见字符提供了已经组合过的形式。

  • 使用预组合标量
let aAcute = "\u{0061}\u{0301}"		// 使用组合标量  	结果:á
let aAcutePrecomposed = "\u{00E1}"	//预组合标量		结果:á

let b = (aAcute == aAcutePrecomposed) // 真

​ aAcutePrecomposed看起来和aAcute的值一样。实际上,如果检查两个字符是否相等,会发现Swift确实认为它们相等。

标准等价是指两个Unicode标量序列在语言学层面是否相等。对于两个字符或者两个字符串,如果它们具备相同的语言学含义和外观,那么无论是否用相同的Unicode变量创建,都认为两者相等。

8.2.3 计算字符数量

​ 使用characters的count属性来判断两个字符串的字符数量。

print("aAcute: \(aAcute.characters.count);
 aAcutePrecomposed: \(aAcutePrecomposed.characters.count)")

​ 运行结果显示字符数量相等:都是1字符长。标准等价意味着无论是用组合标量还是预组合标量,结果都会被当成单个字符。

8.3 访问和修改字符串

8.3.1 索引和区间

​ 可以通过字符串的方法和属性或使用下标语法来访问和修改字符串。

  • 使用下标语法访问String特定索引处的Character。
let greeting = "Guten Tag!"
greeting[greeting.startIndex]	// G
// 可以使用的index(before:)和index(after:)方法访问给定索引之前和之后的索引String
greeting[greeting.index(before: greeting.endIndex)]		// !
greeting[greeting.index(after: greeting.startIndex)]		// u
// 若要访问距离给定索引更远的索引,可以使用index(_:offsetBy:)方法
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]		// a

如果访问超出字符串范围的Character将触发陷阱。

greeting[greeting.endIndex] // Error
  • 使用indices属性可访问字符串中各个字符的所有索引。
for index in greeting.indices {
    print("\(greeting[index]) ", terminator: "")
}
// Prints "G u t e n   T a g ! "
  • 获取区间

    start…end的结果被称为String.CharacterView.Index类型的闭区间。

let start = greeting.startIndex
let end = greeting.index(start, offsetBy: 4)
let range = start...end
let firstFive = greeting[range] // "Guten"
8.3.2 插入和移除
  • 要将单个字符插入指定索引处的字符串中,可以使用insert(_:at:)方法
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)		// "hello!"

注意和append方法的区别,append只能添加到字符串最后。

  • 要从指定索引处的字符串中删除单个字符,使用remove(at:)方法
  • 要在指定范围内删除子字符串,使用removeSubrange(_:)方法
var welcome = "hello swift!"
welcome.remove(at: welcome.index(before: welcome.endIndex))	// "hello swift"

let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)		// "hello"

8.4 子串

​ 当从字符串中获取子字符串时(例如,使用下标或类似prefix(_:)的方法),结果是实例Substring,而不是另一个字符串。

​ Swift中的子字符串与字符串具有大多数相同的方法,这意味着可以像处理字符串一样使用子字符串。但是,在对字符串执行操作时,只能在短时间内使用子字符串。当您准备长时间存储结果时,可以将子字符串转换为的实例String

let greeting = "Hello, world!"
let start = greeting.startIndex
let end = greeting.index(start, offsetBy: 4)
let range = start...end
let beginning = greeting[range]	//	"hello"

let newString = String(beginning)

在这里插入图片描述

9、可空类型(可选项)与 nil

可空类型(optional)是Swift的独特特性,用来指定某个实例可能没有值。如果一个实例没有值,就称其为nil

​ 可以在不存在值的情况下使用optional。可选的代表两种可能性:要么是有一个值,可以解开可选项并访问该值,或者就是没有

9.1 可空类型示例

let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)

​ 上述程序尝试将String装换为Int。但由于可能存在转换失败的情况(例如“aaa”就不能成功转换),因此它返回一个optional Int而不是一个Int。可选Int内容写为Int?而不是Int

这不是相同的类型(和kotlin空安全有本质区别)。问号表示它包含的值是可选的,这意味着它可能包含某个 Int值,或者可能根本不包含任何值

可选项是对其他类型的一层包装,可以理解为一个盒子

如果是nil,那么他是一个空盒子

如果不为nil,那么盒子里装的是:被包装的类型的数据

9.2 可空实例和强制展开

  • 声明可空类型
var errorCodeString: String?		// 默认就是 nil
  • 打印可空类型
print(errorCodeString)
errorCodeString = "404"
print(errorCodeString)

​ 上述代码执行效果如下图所示:
运行效果
​ 可以看到为空时,将打印nil,存在值时会打印 Optional(“404”)。

  • if 语句和强制展开
var errorCodeString: String?
errorCodeString = "404"
if errorCodeString != nil {
    let theError = errorCodeString!
    print(theError)
}

​ 这里增加了if语句,如果errorCodeString不是nil就会执行相应的代码。执行体内的“errorCodeString!”后面的 ! 作用是强制展开。强制展开会访问可空实例封装的值。之所以称之为“强制”展开,是因为无论是否有值,都会访问封装的值。因此,强制展开具有一定的风险,可能会触发陷阱。

9.3 可选项绑定

可选项绑定(optional binding)是一种固定模式,对于判断可选项是否有值很有用。如果有值,就将其赋给一个临时常量或变量,并且使这个常量或变量在条件语句的第一个分支代码中可用。

​ 格式如下:

if let constantName = someOptional {
// 用constantName 做一些事情
}else {
// someOptional 没有值
}

  • 可选项绑定(修改9.2 3代码)
var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString {
    print(theError)
}

​ 上述代码和9.2 3代码执行效果相同,简化了写法,因此,不需要再强制展开可选项了。如果转换成功,那么这个操作已经自动完成了。

​ 假设想把errorCodeString转换成相应的整数形式,可以用嵌套的if let

  • 嵌套的可选项绑定
var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString {
    if let errorCodeInteger = Int(theError) {
        print("\(theError): \(errorCodeInteger)")
    }
}

​ 在第二个绑定中,Int(theError)的结果被展开并赋给errorCodeInteger,使得整数值也可用了。

​ 嵌套可选项绑定可能会显得错综复杂。不过,swift还支持单个if let绑定就可以展开多个可空实例。

  • 展开多个可选项
var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString, let errorCodeInteger = Int(theError)  {
    print("\(theError): \(errorCodeInteger)")
}

​ 现在一行代码展开了两个可选项。

​ 可选项绑定还能进行额外的检查。假设当错误码的值为404时,才处理,如下代码所示。

  • 可选项绑定和额外的检查
var errorCodeString: String?
errorCodeString = "404"
if let theError = errorCodeString, let errorCodeInteger = Int(theError), 
	errorCodeInteger == 404  {
    print("\(theError): \(errorCodeInteger)")
}

​ 现在,如果errorCodeInteger等于404,条件语句就计算为真。只有当两个可空实例都成功展开时,才会执行最后一个子句(errorCodeInteger == 404)。

  • while循环中使用可空实例绑定

    ​ 示例:遍历数组,将遇到的正数加起来,如果遇到负数或者非数字,停止遍历

var strs = ["10","20","abc","-10"]
var index = 0
while let num = Int(strs[index]), num > 0 {
    sum += num
    index += 1
}
print(sum)

9.4 隐式展开可空实例

​ 隐式展开可空类型与普通可空类型类似,只是有一个重要的区别: 它们不需要展开。

​ 确保可选项一直有值。

  • 声明隐式可空类型
var errorCodeString: String!
errorCodeString = "404"
print(errorCodeString)

​ 这里的可空类型是用 ! 声明的,表示这是一个隐式展开可空实例。

​ 不过要注意,这种强大和灵活性也伴随着一定的危险性:如果隐式展开可空实例没有值的话, 访问其值会导致运行时错误。因此,只要某个实例有可能是nil,就别用隐式展开可空实例。

var errorCodeString: String! = nil
let anotherErrorCodeString: String = errorCodeString // 报错
let yetAnotherErrorCodeString = errorCodeString 	// 是可空类型还是隐式展开可空实例

​ 上述代码中,第一个是会触发陷阱,因为anotherErrorCodeString显式声明了类型,不可能是可空实例。第二个,Swift会尽可能推断最安全的类型:普通的可空实例。

​ 它˞主要的应用场景是类初始化。

9.5 可空链式调用

可选链式调用是一种可以在当前值可能为 nil 的可空类型上调用属性、方法及下标的方法。如果可选项有值,那么调用就会成功;如果可选项是 nil ,那么调用将返回 nil 。多个调用可以连接在一起形成一个调用链,如果其中任何一个节点为 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()
  • 原地修改可空实例
upCaseErrorDescription?.append(" PLEASE TRY AGAIN.")

9.6 nil合并运算符 ??

a ?? b

a 必须为可空实例;

b 可以为可选项 或者 不是可选项

a 和b 的存储类型必须相同

  • 如果a不为nil,就返回a,否则返回b;如果b不是可选项,返回a是会自动解包
  • 实例:分析 c 的类型和值
let a: Int? = 1
let b: Int? = 2
let c = a ?? b

// c 是 Int?,Optional(1)
let a: Int? = nil
let b: Int? = 2
let c = a ?? b

// c 是 Int?,Optional(2)
let a: Int? = nil
let b: Int? = nil
let c = a ?? b

// c 是 Int?, nil
let a: Int? = 1
let b: Int = 2
let c = a ?? b		// 如果b不是可选项,返回a时会自动解包

// c 是 Int,1
let a: Int? = nil
let b: Int = 2
let c = a ?? b

// c 是 Int,2
  • 多个 ?? 一起使用,从左往右运算
let a: Int? = 1
let b: Int? = 2
let c = a ?? b ?? 3

// c 是 Int,1
let a: Int? = nil
let b: Int? = 2
let c = a ?? b ?? 3

// c 是 Int,2
  • ??if let配合使用
let a: Int? = nil
let b: Int? = 2
if let c = a ?? b {
    print(c)
}
// 类似于if a != nil || b != nil

if let c = a, let d = b {
    pritn(c)
    print(d)
}
// 类似于if a != nil && b != nil

下一章 :流程控制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值