Swift笔记(一):可选类型、语法基础
前言:
本文主要讲述:
- Swift 语法中的最基础的数据类型,包括常量变量声明、 Int、Float等;
- 重点讲述元祖和可选类型;
一、Swift 和 OC 对比概览
官方文档中首先表明了 Swift 和 OC的几个主要的区别或者内容的升级,如下:
1. Swift 包含了 C 和 Objective-C 上所有基础数据类型;
基础类型有:
- Int 表示整型值,对应 OC 中 NSInteger;
- Double 和 Float 表示浮点型值,对应 CGFloat ;
- Bool 是布尔型值,对应BOOL;
- String 是文本型数据,对应NSString,但是不严格,因为NSString 是对象,并不是基础类型;
- Array、Set 和 Dictionary 是集合类型,对应 OC 中的 NSArray、NSSet、NSDictionary;
2. Swift 更推荐使用 let
来表示的常量;
推荐使用的原因是:
- 更加安全,体现在 Swift 编译阶段的检查比 OC 更加严格和清晰;
- 语义更加清晰易读;
3. Swift 中新增高阶基础类型:元祖;
元祖的优势;
- 可以创建或者传递一组数据;
- 可以再函数中返回多个值(相比于 OC 返回字典或者数组,很有用);
4. Swift新增可选类型;
可选类型是 Swift 的一大特色,甚至是 Swift 很多语言特性的基础,以后的章节会详细讲到;
5. Swift 是强语言类型,类型安全;
这个特性在写代码的时候可以深刻感受到其好处,编译时的报错更加详细和严格,不会像 OC 中,很多报错新手看到之后都是一脸懵逼,这也是 Swift 更容易上手的原因之一。
二、常量和变量
Swift 会在变量声明时、第一次赋值时、注解时推断或者主动确定变量的类型,从而在编译阶段就检查变量的类型,如果不一致就报错。
1.常量和变量的声明
let声明常量,var声明变量:
let maximumNumberOfLoginAttempts = 10
var currentLoginAttempt = 0
也可以一行中声明多个常量或者变量:
var x = 0.0, y = 0.0, z = 0.0
2.类型注解
类型注解用于说明常量或者变量中要存储的值的类型:
var welcomeMessage: String
一行中定义多个变量:
var red, green, blue: Double
3.常量和变量的命名
这个就不多说了,虽然 Swift 中可以使用任意Unicode来命名,甚至是表情符号,但是开发中,我们是不会这么做的,仍然使用标准的驼峰命名。
三、整数与浮点
1.整数类型
Swift 提供了8、16、32和64位的有符号和无符号整数类型。这些整数类型和 C 语言的命名方式很像,比如8位无符号整数类型是 UInt8,32位有符号整数类型是 Int32 。
但是,通常我们只需要使用 UInt 和 Int就行了,其长度如下:
- 在32位平台上,Int 和 Int32 长度相同。
- 在64位平台上,Int 和 Int64 长度相同。
- 在32位平台上,UInt 和 UInt32 长度相同。
- 在64位平台上,UInt 和 UInt64 长度相同。
2.整数范围
Swift 中可以获取对应整数类型众的最大最小值:
let minValue = UInt8.min // minValue 为 0,是 UInt8 类型
let maxValue = UInt8.max // maxValue 为 255,是 UInt8 类型
3.浮点型
Double 表示64位浮点数,Float 表示32位浮点数,不多说了。
四、字面量
Swift 中的整数和浮点数有很丰富的表达方式:
1.下划线
数值类字面量可以包括额外的格式来增强可读性。整数和浮点数都可以添加额外的零并且包含下划线,并不会影响字面量:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
2.进制
如下:
let decimalInteger = 17
let binaryInteger = 0b10001 // 二进制的17
let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 十六进制的17
浮点类型也可以用十六进制表示,但是小数点两边必须有至少一个十进制数字(或者是十六进制的数字):
// 12.1875
let hexadecimalDouble = 0xC.3p0
3.指数
十进制才能使用,如下:
// 12.185的各种表达
let decimalDouble = 12.1875
let exponentDouble = 1.21875e1
let exponentDouble2 = 121.875e-2
// 十六进制浮点数必须有一个指数,通过大写或者小写的 p 来指定。
let hexadecimalDouble = 0xC.3p0(0xC.3 * 2^0)
五、补充
1. 起别名
类似于 C 语言中的 typedef ,可以使用 typealias 来起别名:
typealias AudioSample = UInt16
var maxAmplitudeFound = AudioSample.min
2. 布尔值
OC 中会非0则为真,也就是 YES,Swift 中只有 true 和 false,且如果使用 OC 中的非0即为真的逻辑来判断,则会报错:
let i = 1
if i {// 这个例子不会通过编译,会报错
}
只能使用严格的 bool 类型来做判断:
let i = 1
if i == 1 {// 这个例子会编译成功
}
六、元祖
元组(tuples)把多个值组合成一个复合值。元组内的值可以是任意类型,并不要求是相同类型。
1.元祖的定义和声明
元祖使用小括号来创建:
let http404Error = (404, "Not Found")
也可以给元祖中的元素起名:
let http200Status = (statusCode: 200, description: "OK")
print("\(http200Status.statusCode),\(http200Status.description)")
2.元祖的访问
下标访问:
print("The status code is \(http404Error.0)")
// 输出“The status code is 404”
print("The status message is \(http404Error.1)")
// 输出“The status message is Not Found”
元素名访问
let http200Status = (statusCode: 200, description: "OK")
print("\(http200Status.statusCode),\(http200Status.description)")
3.元祖的分解
这里使用let + () 的形式拆解元祖中的元素,只不过,这里要怎么理解呢?是新创建了一个带元素命名的元祖,还是新创建了两个变量?暂时后者的理解比较符合,因为如果是新创建的元祖,访问其元素需要使用点语法,具体操作如下:
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
// 输出“The status code is 404”
print("The status message is \(statusMessage)")
// 输出“The status message is Not Found”
还可以使用下划线来忽略元素:
let (justTheStatusCode, _) = http404Error
print("The status code is \(justTheStatusCode)")
// 输出“The status code is 404”
七、可选类型
格式:type + ?,例如:Int?、Bool?
1. 意义和理解
因为 Swift 是强语言,也就是说所有的变量在编译阶段就需要确定值和类型,其本质是对变量进行初始化操作,否则会报错,如下:
但是在实际开发中,我们经常会遇到需要初始化变量,但是预留变量的值,在一些逻辑之后才给变量赋值的情况,所以才有了可选类型的出现。
可选类型可以看成是对非可选类型的的一种限制和加强,体现在:
- 语义加强:这个变量可能有值,也可能没有值,要特殊注意;
- 行为限制:这个变量只有在通过可选绑定或者强制解析之后,确定其真实值之后才能正常使用(当做非可选值使用)(本质上仍然是强语言特性的一种体现);
这种限制的直接结果就是:
- 可选类型之间可以相互赋值,如图不报错:
- 可选类型在可选绑定或强制解析之后才可以赋值给费可选类型,如图:
结论:可选类型是一种不成熟的非可选类型,需要在一定条件下(可选绑定/强制解析)才可以当做非可选类型使用。
2.nil
nil 用于给可选变量赋值,表示没有值,nil 不能用于非可选的常量和变量。
如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。如下:
var code = 404
code = nil
print(code)
报错:
error: 'nil' cannot be assigned to type 'Int'
3. 可选绑定
需要将可选绑定理解成 Swift 中的一种特殊的,单独的语法,因为 Swift 中的 if 的使用并不是这么用,且不能这么用。
if 正常的使用方法是:
if Bool值 {// true逻辑
}
也就是说 if 条件中只能是 Bool 值。
OC 中的可以受用非 0 值表示布尔值中的“真”,但是 Swift 中,Bool 值只有两个:false 和 true。所以理论上,if 中的条件是不能像 OC 中使用变量来进行布尔判断的,而可选绑定看上去却是利用一个形参来进行布尔判断,所以就需要把可选绑定理解成 Swift 中的一种特殊的,区别于 if 控制流的一种单独的语法,其语法为:
if 形参 = 要绑定的可选变量 {// true 的逻辑中可以使用上述中的新声明的变量...使用形参...
} else {// false 的逻辑中不可以使用上述中的新声明的变量,会报错}
从一个报错的提示中可以证明这个观点:
还可以从另外一个方面证明:
var optionalString: String?
optionalString = "123"
if var actualString = optionalString {print(actualString) // 123print(type(of: actualString)) // Stringprint(optionalString) // Optional("123")print(type(of: optionalString)) // Optional<String>
}
从打印结果中可以看出,这个语法做了两个操作:
- 对 optionalString 进行了解析;
- 使用 actualString 进行了 if 结果的判断;
总结:
3. false 逻辑中不能使用形参;
4. 形参使用 var 和 let 修饰都可以;
5. 可选绑定语法中的实参必须是可选类型;
4. 强制解析
强制解析没什么可说的,使用!
进行强制解析,只是需要注意的是,如果可选类型的值为nil
,强制解析会导致程序崩溃,所以一般会使用 ??
来设置一个默认值:
var optionalString: String?
print(optionalString ?? "123")
5.隐式解析可选类型
意义:第一次被赋值之后,可以确定一个可选类型总会有值。
理解:你可以把隐式解析可选类型当做一个可以自动解析的可选类型,在第一次被赋值之后使用时,就会被自动解析;
一个隐式解析可选类型其实就是一个普通的可选类型,但是可以被当做非可选类型来使用,并不需要每次都使用解析来获取可选值。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感叹号来获取值let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感叹号
八、特殊运算符
1.赋值运算符
- 赋值符(=)不再有返回值,这样就消除了手误将判等运算符(==)写成赋值符导致代码错误的缺陷,所以不再会出现通过赋值之后的结果来判断,如下:
if x = y {// 此句错误,因为 x = y 并不返回任何值
}
- 赋值运算符新增元祖支持,如果赋值的右边是一个多元组,它的元素可以马上被分解成多个常量或变量:
let (x, y) = (1, 2)
// 现在 x 等于 1,y 等于 2
备注: 这里的等号相当于元祖分解,新建了两个变量x和y并进行了赋值操作。
2.溢出运算
算术运算符(+,-,*,/,% 等)的结果会被检测并禁止值溢出(编译报错),以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果,如下:
然而,当你希望的时候也可以选择让系统在数值溢出的时候采取截断处理,而非报错。Swift 提供的三个溢出运算符来让系统支持整数溢出运算。这些运算符都是以 & 开头的:
- 溢出加法 &+
- 溢出减法 &-
- 溢出乘法 &*
这里不展开讲解,暂时知道有这么个东西就行
3.恒等运算符
使用===
判断两个对象是否引用同一个对象实例。
延伸:Swift 中的面向对象是怎么定义的?基础数据和是对象吗?区别是什么?值类型和引用类型和对象的关系?后面会讲到。
4.元祖比较
元祖比较有几个重点:
- 如果两个元组的元素相同,且长度相同的话,元组就可以被比较。
- 比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止,且此时的比较结果就作为了整个元祖的比较结果,后面元素的大小不再对元祖大小起作用。(从左到右优先原则)
- 如果所有的值都相等,那么这一对元组我们就称它们是相等的。
举例如下:
(1, "zebra") < (2, "apple") // true,因为 1 小于 2,结果确定
(3, "apple") < (3, "bird") // true,因为 3 等于 3,但是 apple 小于 bird
(4, "dog") == (4, "dog") // true,因为 4 等于 4,dog 等于 dog
5.空和运算符(重要)
空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回一个默认值 b。(备胎)
一般和可选类型一起使用,防止强制解析引起的崩溃问题。
空和运算符相当于:
a != nil ? a! : b
6.区间运算符(重要)
区间运算符算是对于范围的简写,好用。种类如下:
- 闭区间运算符
三个点:
for index in 1...5 {print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
- 半开区间运算符
两个点+大小于号:
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {print("第 \(i + 1) 个人叫 \(names[i])")
}
// 第 1 个人叫 Anna
// 第 2 个人叫 Alex
// 第 3 个人叫 Brian
// 第 4 个人叫 Jack
- 单侧区间
三个点
for name in names[2...] {print(name)
}
// Brian
// Jackfor name in names[...2] {print(name)
}
// Anna
// Alex
// Brian
- 半开单侧区间
两个点+大小于号
for name in names[..<2] {print(name)
}
// Anna
// Alex
九、集合
Swift 语言提供数组(Array)、集合(Set)和字典(Dictionary)三种基本的集合类型用来存储集合数据,注意点:
- 数组是有序数据的集,集合是无序无重复数据的集,字典是无序的键值对的集;
- 数组的可变与否和类无关,而是取决于数组用
let
修饰还是用var
修饰;
有别于 OC ,Swift 中的集合只能存储一种类型的值。严格意义上讲,OC 中的数组、集合、字典也只能存储一种类型的值,那就是 NSObject 类。只是 OC 中所有的类都是继承自 NSObject ,Swift 中没有统一的基类,所以给人的第一感觉是 OC 中的数组比较特别,但本质是一样的。
延伸:
那么 Swift 中如何存储多类型的值呢?是否有必要?
Array是对象吗?为什么其原理是一个public struct Array<Element>
?
1. 数组
1.1 数组的创建
有三种方法创建数组,但是其本质都是一样的,是对象的构造方法来创建:
// [类型]()
var someInts = [Int]()// 直接创建
var shoppingList: [String] = ["Eggs", "Milk"]
// 类型可以省略,Swift会自己判断类型
var threeInts = [1, 2, 3]// Array正式的构造方法
var threeDoubles = Array(repeating: 0.0, count: 3)
1.2 数组的修改
基本的修改方法:
// append
shoppingList.append("Flour")// insert(_:at:)
shoppingList.insert("Maple Syrup", at: 0)// remove(at:)
let mapleSyrup = shoppingList.remove(at: 0)// removeLast()
let apples = shoppingList.removeLast()
算数运算符 +=
、+
可以直接操作数组;
var shoppingList = ["Eggs", "Milk"]
shoppingList.append("Flour")// 算数运算符 +=、+
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]// 打印结果为7项:["Eggs", "Milk", "Flour", "Baking Powder", "Chocolate Spread", "Cheese", "Butter"]
print(shoppingList)
区间运算符本质是数组中部分元素的整体替换,既可以增加数组,也可以减少数组,功能更加强大;
// 相当于[shoppingList[4], shoppingList[5], shoppingList[6]] = ["Bananas", "Apples"]
shoppingList[4...6] = ["Bananas", "Apples"]// 结果只有6项,因为最后4-6的三个元素中的"Butter"被去掉了
// ["Eggs", "Milk", "Flour", "Baking Powder", "Bananas", "Apples"]
print(shoppingList)
1.3 数组的遍历
主要是两种遍历方式:
// for in 普通遍历,获取数组中的元素
for item in shoppingList {print(item)
}// enumerated() 返回一个由索引值和数据值组成的元组数组
for (index, value) in shoppingList.enumerated() {print("Item \(String(index + 1)): \(value)")
}
1.4 备注
Array 中的还有很多其他的属性和方法可以实现更加丰富的功能,可以自行参阅文档,使用 Jump to Definition 即可查看。
2. 集合
集合有两个特点:
- 值唯一
- 无序
2.1 哈希值
一个类型为了存储在集合中,该类型必须是可哈希化的——也就是说,该类型必须提供一个方法来计算它的哈希值。一个哈希值是 Int 类型的,相等的对象哈希值必须相同,比如 a == b
,因此必须 a.hashValue == b.hashValue
。
Swift 的所有基本类型(比如 String
、Int
、Double
和 Bool
)默认都是可哈希化的,可以作为集合值的类型或者字典键的类型。没有关联值的枚举成员值(在枚举有讲述)默认也是可哈希化的。
想要使用 Set 存储自定义的对象,就需要遵守 Hashable 协议:
class Person: Hashable {var name:String?static func == (lhs: Person, rhs: Person) -> Bool {// 具体实现return false}func hash(into hasher: inout Hasher) {// 具体实现}
}
使用:
var p = Person()
var set:Set<Person> = [p]
print(array)
2.2 Set的创建
// 创建空集合
var letters = Set<Character>()// 创建字面量集合(重要:一定要写: Set<String>,否则就成数组了)
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// <类型>可以省略
var favoriteGenres: Set = ["Rock", "Classical", "Hip hop"]
2.3 集合的操作
集合的操作方法省略,具体参考API文档即可;
3. 字典
字典中的 key 也需要和 Set 一样,遵循 Hashable 协议才能被当做 key。
3.1 字典的创建
// 构造方法
var namesOfIntegers = [Int: String]()// 简化语法字面量
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// 类型可省略:
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
字典操作
需要注意的是可以是用赋值nil
来删除键值对:
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
airports["APL"] = "Apple Internation"// 删除
airports["APL"] = nil
// 等效于
airports.removeValue(forKey: "APL")
字典遍历
每一个字典中的数据项都以 (key, value) 元组形式返回,并且可以使用临时常量或者变量来分解这些元组:
for (airportCode, airportName) in airports {print("\(airportCode): \(airportName)")
}
// YYZ: Toronto Pearson
// LHR: London Heathrow
keys
和values
也可以遍历:
for airportCode in airports.keys {print("Airport code: \(airportCode)")
}
// Airport code: YYZ
// Airport code: LHRfor airportName in airports.values {print("Airport name: \(airportName)")
}
// Airport name: Toronto Pearson
// Airport name: London Heathrow
Swift笔记(一):可选类型、语法基础相关推荐
- 初探swift语言的学习笔记二(可选类型?和隐式可选类型!)
作者:fengsh998 原文地址:http://blog.csdn.net/fengsh998/article/details/28904115 转载请注明出处 如果觉得文章对你有所帮助,请通过留言 ...
- Swift中的可选类型(Optional)
为什么80%的码农都做不了架构师?>>> Swift中的可选类型(Optional) ##什么是Optional Optional在Swift中表示可选类型,那么什么是可选类型 ...
- iOS开发笔记之九十四——Swift中的可选类型Optional
一.Optional类型的概念 Swift中为何要引入Optional的概念,官方文档中有介绍: Swift also introduces optional types, which handle ...
- Swift中关于可选类型(?)与强制解析(!)的特性
2019独角兽企业重金招聘Python工程师标准>>> Swift中问号表示这是一个可选类型,白话翻译:某个常量或者变量可能是一个类型,也可能什么都没有,我不确定它是否真的会有值,也 ...
- Object-C---gt;Swift之(二)可选类型
可选和nil的恋情 先看一个例子吧!let str="hello world"let num:Int=Int(str) 这两行代码的意思str字符串转换成Int类型,嘿嘿!这不是坑 ...
- java笔记(第一部分语法基础)
java笔记 一. 计算机概述 1.1 计算机组成部分 1.2 人机交互方式 1.3 计算机语言 1.4 Java语言介绍 1.5 Java开发环境搭建 二.基本数据类型与运算 2.1 关键字 2.2 ...
- Kt学习笔记(一) 语法基础
文章目录 一.基本语法 1.1 定义变量 1.2 定义方法 1.3 注解的使用 二.基本数据类型 2.1数据类型 2.2 字符类型 2.3 Boolean类型 2.4 数组 2.5 字符串 2.6 字 ...
- ggplot2学习笔记2:ggplot()语法基础
此博客作为自己的学习笔记,同时与大家交流分享! Mastering the Grammar 语法这一部分有点难理解,只是看文字描述的概念不易懂,最好是能在画图过程中去理解体会,只有了解了语法,才能掌握 ...
- 读书笔记(06) - 语法基础 - JavaScript高级程序设计
写在开头 本篇是小红书笔记的第六篇,也许你会奇怪第六篇笔记才写语法基础,笔者是不是穿越了. 答案当然是没有,笔者在此分享自己的阅读心得,不少人翻书都是从头开始,结果永远就只在前几章. 对此,笔者换了随 ...
- swift可选类型_Swift可选
swift可选类型 Swift Optional is a very important underlying concept in Swift programming. Optional is so ...
最新文章
- Filter获取Spring Bean对象
- Visual studio那些破事。。。(生成静态库、生成动态库、引用静态库、引用动态库)
- 利用JavaScript检查用户注册信息是否正确,在以下情况不满足时报错并阻止提交表单...
- servlet精华讲解
- 离散数学及其应用第六版中文电子书和答案
- jeecg框架 弹出框问题
- python语言是不是多模型语言_Python模型转换为Modelica模型的方法与流程
- win10下快速搭建metasploitable3教程-简单版-已避坑
- 一个 JDBC 实现对 mysql 进行分页查询的 实例
- 郭德纲的网络效应和网络利用
- npm下载什么都报错ERRO -4048
- Arduino 开锁,刷卡开锁模块
- pdf文件的简单介绍-提取pdf中的内容
- vue2和vue3的参数接受方式以及vue中url参数解码
- iOS编译速度如何稳定提高10倍以上
- 基于Vue的驾校理论课模拟考试系统
- OpenWrt安装ser2net.ipk软件包
- 跨平台攻击令智能设备的物理隔离和系统屏障消失
- MySql学习(七)排序和分页(order by limit),及存在的坑
- 计算机可视化视景仿真,计算与仿真、三维设计、图像处理、视景仿真、4k视频剪辑工作站介绍2015版.pptx...