标识符
如果一定要用关键字作为标识符,可以在关键字前后加上重音符(`)
字面量
像特定的数字 布尔值或者字符串,能够直接表明自己的类型并为变量赋值的值
是一个全局函数
public func print(items: Any..., separator: String = default, terminator: String = default)
如果需要输出的内容不换行,那么我们可以把最后一个参数terminator设置为空字符串
for x in 0...10{
print("\(x) ", terminator: "")
}
print()
如果需要接受用户的输入可以使用
let input = readLine()
数据类型
Int
整数类型
长度与当前平台的原生长度相同
可以提高代码的一致性和可复用性
UInt
无符号类型
长度与当前平台的原生长度相同
最好不要创建UInt数据,除非你真的需要一个与当前平台的原生长度相同的无符号数据,使用Int创建整数类型,可以提高代码的可复用性,有利于不同类型的数据之间的类型转换以及可用于进行类型判断
Float Double
Double 表示64位浮点数,当你需要存储很大或者很高精度的浮点数时使用
Float 表示32位浮点数 精度要求不高的时候使用
Double 最少有15位数 Float 至少六位数
字符串
字符串是字符的序列集合
数值范围
image.png类型别名
对当前类型定义了另一个名字 使用typealias关键字来定义
typealias newname = type
类型安全
Swift是一个类型安全的语言 会在编译的时候进行类型检查 并把不匹配的类型标记为错误。这可以让你
在开发的时候尽早发现并修复错误。
类型推断
当声明变量或者常量的时候,如果你没有显示的指定类型,那么Swift会根据类型推断 为你的常量或变量
选择合适的类型
变量
变量声明是告诉编译器在内存中的哪个位置为变量创建多大的存储空间
变量命名
变量命名以字母 下划线和数字组成
需要以字母或者下划线开始
变量名也可以使用简单的 Unicode 字符
import Cocoa
var _var = "Hello, Swift!"
print(_var)
var 你好 = "你好世界"
var 菜鸟教程 = "www.runoob.com"
print(你好)
print(菜鸟教程)
变量输出
变量或常量都可以用print输出
在字符串中可以使用括号和反斜线来插入变量
import Cocoa
var name = "菜鸟教程"
var site = "http://www.runoob.com"
print("\(name)的官网地址为:\(site)")
可选类型 optionals
Swift 的可选(Optional)类型,用于处理值缺失的情况。可选表示"那儿有一个值,并且它等于 x "
或者"那儿没有值"。
强制解析
自动解析
可选绑定
使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含就把值赋给一个临时常量或者变量。可
选绑定可以用在if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。
import Cocoa
var myString:String?
myString = "Hello, Swift!"
if let yourString = myString {
print("你的字符串值为 - \(yourString)")
}else{
print("你的字符串没有值")
}
常量
常量一旦设定 在程序运行时就无法改变其值
变量的值可以随意改变
类型标注
当你声明常量或者变量的时候,可以加上类型标注 类型标注就是要说明常量或者变量中要存储的值的类型
如果需要添加类型标注,那么需要在常量或者变量后面加上一个冒号和一个空格 然后加上类型名称
import Cocoa
let constA = 42
print(constA)
let constB:Float = 3.14159
print(constB)
常量命名
常量的命名以字母 数字和下划线组成 其中,需要以下划线或者字母开始
常量输出
变量和常量可以使用 print(swift 2 将 print 替换了 println) 函数来输出。
在字符串中可以使用括号与反斜线来插入常量
字面量
字符串字面量
字符串型字面量中不能包含未转义的双引号 (")、未转义的反斜线(\)、回车符或换行符。
WeChatbaa76ca94ae6cbf5ddff36943b174564.png
运算符
算术运算符
比较运算符
逻辑运算符
位运算符
用来对二进制位进行运算
~ 取反
& 按位与
| 按位与或
^ 按位异或
WeChat3ee697484ec00a9b3173f1ea5e70b8f6.png
进行位运算
1.png
2.png
赋值运算符
WeChat828c9497d6c6640789c84db296b74385.png区间运算符
WeChat74f2231cb536a28842d7a2d8788829c7.png其他运算符
1.一元运算符对单一操作对象操作(如-a)。一元运算符分前置运算符和后置运算符,前置运算符需紧跟在操作对象之前
(如!b),后置运算符需紧跟在操作对象之后(例如c!)。备注:在Java/C没有类似c!的语法, 在Swift中用在
Optional类型取值。
2.二元运算符操作两个操作对象(如2 + 3),是中置的,因为它们出现在两个操作对象之间。
3.三元运算符操作三个操作对象,和 C 语言一样,Swift 只有一个三元运算符,就是三目运算符(a ? b : c)。
WeChatd120fc57f2774bcf574c8a06fba8aaac.png
运算符优先级
指针最优,单目运算优于双目运算。如正负号。
先乘除(模),后加减。
先算术运算,后移位运算,最后位运算。请特别注意:1 << 3 + 2 & 7 等价于 (1 << (3 + 2))&7
逻辑运算最后计算
WeChat0a25c991fc2fa2c35f519f8575d585b9.png
循环
循环控制语句
WeChat3b4e7e61fdb9478a1aa081ef1aa36971.png字符串
Unicode 字符串
unicodeScalars
通过遍历字符串的unicodeScalars属性来访问它的 Unicode 标量编码.
<
比较两个字符串,对两个字符串的字母逐一比较。
import Cocoa
var unicodeString = "菜鸟教程"
print("UTF-8 编码: ")
for code in unicodeString.utf8 {
print("\(code) ")
}
print("\n")
print("UTF-16 编码: ")
for code in unicodeString.utf16 {
print("\(code) ")
}
UTF-8 编码:
232
143
156
233
184
159
230
149
153
231
168
139
UTF-16 编码:
33756
40479
25945
31243
字符(Character)
不能创建空的字符类型的变量或者常量
可通过for-in循环来遍历字符串中的characters属性来获取每一个字符的值
import Cocoa
for ch in "Runoob".characters {
print(ch)
}
数组
创建数组赋值给一个变量 那么数组是可以改变的
创建的数组赋值给一个常量,数组是不可以改变的 大小 数组成员
我们可以通过只读属性 isEmpty 来判断数组是否为空,返回布尔值
字典
var someDict = [KeyType: ValueType]()
我们可以使用 updateValue(forKey:) 增加或更新字典的内容。如果 key 不存在,则添加值,如果存在则修改 key
对应的值。updateValue(_:forKey:)方法返回Optional值。
import Cocoa
var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
var oldVal = someDict.updateValue("One 新的值", forKey: 1)
var someVar = someDict[1]
print( "key = 1 旧的值 \(oldVal)" )
print( "key = 1 的值为 \(someVar)" )
print( "key = 2 的值为 \(someDict[2])" )
print( "key = 3 的值为 \(someDict[3])"
1.我们可以使用 removeValueForKey() 方法来移除字典 key-value 对。如果 key 存在该方法返回移除的值,如果不
存在返回 nil 。
2.可以通过直接赋值给相应key对于的value为nil来移除这个key-value对
遍历字典
1.使用for-in遍历字典
2.使用enumerate()遍历字典 返回字典的索引和key value对
import Cocoa
var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
for (key, value) in someDict.enumerated() {
print("字典 key \(key) - 字典 (key, value) 对 \(value)")
}
字典转为数组
import Cocoa
var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
let dictKeys = [Int](someDict.keys)
let dictValues = [String](someDict.values)
print("输出字典的键(key)")
for (key) in dictKeys {
print("\(key)")
}
print("输出字典的值(value)")
for (value) in dictValues {
print("\(value)")
}
函数
是用来完成特定任务的独立的代码块
函数声明 用来声明函数的名字 返回类型和参数
函数定义 提供了函数的实体
func funcname(形参) -> returntype
{
Statement1
Statement2
……
Statement N
return parameters
}
我们可以通过函数名以及对应类型的参数值来调用函数,函数的参数传递的顺序必须与参数列表相同。
元祖作为函数返回值
元祖和数组类似,不同的是元祖中的元素可以是任意类型,使用的是圆括号
可以使用元祖类型tuple 让多个值作为一个复合值从函数中返回
mport Cocoa
func minMax(array: [Int]) -> (min: Int, max: Int) {
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)
}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("最小值为 \(bounds.min) ,最大值为 \(bounds.max)")
如果你不确定返回的元组一定不为nil,那么你可以返回一个可选的元组类型。
你可以通过在元组类型的右括号后放置一个问号来定义一个可选元组,例如(Int, Int)?或(String, Int, Bool)?
可选元祖类型如(Int ,Int)?与元祖包含可选类型如(Int?,Int?)是不同的,可选的元祖类型,是整个元祖都是可选的,不只是元祖中的每个元素值
import Cocoa
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)")
}
函数参数名称
函数参数都有一个外部参数名和一个局部参数名
局部参数名在函数的实现内部使用
可以在局部参数名前面加上外部参数名 用空格分隔 外部参数名用于在函数调用的时候传递给函数的参数
如果使用了外部参数名,那么,在函数调用的时候,必须使用外部参数名
可变参数
可变参数可以接受零个或多个参数,函数调用的时候,你可以用可变参数指定函数参数,其数量是不固定的
可变参数通过在变量类型名的后面加(...)的方式来定义
import Cocoa
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: "Google", "Baidu", "Runoob")
常量 变量 I/O参数
一般默认在函数中定义的参数都是常量参数,只可以查询使用,不能用来改变他的值
如果要声明一个变量参数,可以在参数定义前面加inout参数,这样可以改变这个参数的值
func getName(_ name: inout String).........
一般默认的参数都是传值调用的,不是传引用,所以传入的参数改变,并不影响原来的那个参数,传入的参数只是原来的副 本,当传人的参数作为输入输出参数时,需要在参数前面加&,表示这个参数可以被函数修改
import Cocoa
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var x = 1
var y = 5
swapTwoInts(&x, &y)
print("x 现在的值 \(x), y 现在的值 \(y)")
x 现在的值 5, y 现在的值 1
函数类型及使用
每个函数都有特定的类型,由参数类型和返回值类型组成
使用函数类型和其他类型一样
可以定义一个类型为函数的常量或者变量,并将适当的函数赋值给他
import Cocoa
func sum(a: Int, b: Int) -> Int {
return a + b
}
var addition: (Int, Int) -> Int = sum
print("输出结果: \(addition(40, 89))")
输出结果: 129
函数类型作为参数类型、函数类型作为返回类型
import Cocoa
func sum(a: Int, b: Int) -> Int {
return a + b
}
var addition: (Int, Int) -> Int = sum
print("输出结果: \(addition(40, 89))")
func another(addition: (Int, Int) -> Int, a: Int, b: Int) {
print("输出结果: \(addition(a, b))")
}
another(addition: sum, a: 10, b: 20)
输出结果: 129
输出结果: 30
函数嵌套
函数内部定义一个新的函数,外部的函数可以调用函数内部定义的函数
import Cocoa
func calcDecrement(forDecrement total: Int) -> () -> Int {
var overallDecrement = 0
func decrementer() -> Int {
overallDecrement -= total
return overallDecrement
}
return decrementer
}
let decrem = calcDecrement(forDecrement: 30)
print(decrem())
-30
闭包
是自包含的功能代码块,可以用来在代码中使用或者作为参数传值
闭包的形式
WeChatb547379b7c5c65da39ab570e254bcbc7.png{(parameters) -> return type in
statements
}
闭包表达式是一种利用简洁语法构建内联闭包的方式
sorted 方法
sorted(by:) 会根据您提供的用于排序的闭包函数将已知类型的数组中的值进行排序
排序完成之后,会返回一个跟原先数组元素相同 数量相同但是排序之后的新数组,原先的数组不会被修改
传入的参数
1.已知类型的数组
2.闭包函数-->该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型的值来表明当排序结束后传入的第一
个参数是排在第二个参数的后面还是前面,如果第一个参数排在第二个参数前面,那么排序闭包函数需要返回true ,否则
是false
import Cocoa
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)
["S", "D", "BE", "AT", "AE"]
参数名称缩写
自动为内联函数提供了参数名称缩写功能,可以直接通过$0,$1,$2来顺序调用闭包的参数。
运算符函数
Swift 的String类型定义了关于大于号 (>) 的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类
型的值。 而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。 因此,您可以简单地传递一个大于号,Swift
可以自动推断出您想使用大于号的字符串函数实现:
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
var reversed = names.sorted(by: >)
print(reversed)
["S", "D", "BE", "AT", "AE"]
尾随闭包
是一个写在函数括号之后的一个闭包表达式,函数支持将其作为最后一个参数调用
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函数体部分
}
// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure({
// 闭包主体部分
})
// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
// 闭包主体部分
}
import Cocoa
let names = ["AT", "AE", "D", "S", "BE"]
//尾随闭包
var reversed = names.sorted() { $0 > $1 }
print(reversed)
捕获值
闭包可以在其定义的上下文中捕获常量或者变量
即使定义这些常量或者变量的原域已经不存在了,闭包仍然可以在闭包函数体中引用和修改这些值
import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
// 返回的值为10
print(incrementByTen())
// 返回的值为20
print(incrementByTen())
// 返回的值为30
print(incrementByTen())
闭包是引用类型
无论将闭包/函数赋值给一个常量或者变量,实际上是将常量/变量的值设置为对应闭包/函数的引用
如果将闭包赋值给2个不同的变量/常量,两个值会指向同一个闭包
import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() -> Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
// 返回的值为10
incrementByTen()
// 返回的值为20
incrementByTen()
// 返回的值为30
incrementByTen()
// 返回的值为40
incrementByTen()
let alsoIncrementByTen = incrementByTen
// 返回的值也为50
print(alsoIncrementByTen())
枚举
是一组有共同特性的数据的集合
功能
1.声明在类中,可以通过实例化来访问他的值
2.枚举也可以定义构造函数来提供一个初始成员值 可以在原始的实现基础上扩展他们的功能
3.可以遵守协议来实现标准的功能
语法
enum enumname {
// 枚举定义放在这里
}
枚举成员在被创建的时候不会被赋予一个默认的整形值,是已经明确定义好的枚举名称enumname 类型
相关值和原始值的区别
WeChat3c510d766929a6fb221c5a6ec30b857d.png相关值
import Cocoa
enum Student{
case Name(String)
case Mark(Int,Int,Int)
}
var studDetails = Student.Name("Runoob")
var studMarks = Student.Mark(98,97,95)
switch studMarks {
case .Name(let studName):
print("学生的名字是: \(studName)。")
case .Mark(let Mark1, let Mark2, let Mark3):
print("学生的成绩是: \(Mark1),\(Mark2),\(Mark3)。")
}
学生的成绩是: 98,97,95。
原始值可以是字符串,字符,或者任何整型值或浮点型值。每个原始值在它的枚举声明中必须是唯一的。
在原始值为整数的枚举时,不需要显式的为每一个成员赋值,Swift会自动为你赋值。如果第一个值没有被赋初值,将会被自动置为0
结构体
构造代码所用的一种通用且灵活的构造体
结构体不需要包含实现文件和接口
结构体允许我们创建一个单一文件,且系统会自动生成面向其他代码的外部接口
结构体总是通过复制的方式在代码中传递,所以,他的值是不可以修改的
语法
struct nameStruct {
Definition 1
Definition 2
……
Definition N
}
import Cocoa
struct studentMarks {
var mark1 = 100
var mark2 = 78
var mark3 = 98
}
let marks = studentMarks()
print("Mark1 是 \(marks.mark1)")
print("Mark2 是 \(marks.mark2)")
print("Mark3 是 \(marks.mark3)")
应用
1.结构体的目的是用来封装少量相关简单数据值
2.有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是引用
3.任何在结构体中存储的值类型数据,也将会被拷贝 而不是被引用
4.结构体不需要去继承另一个已存在类型的属性或者行为
列子
几何形状的大小,封装一个width属性和height属性,两者均为Double类型。
一定范围内的路径,封装一个start属性和length属性,两者均为Int类型。
三维坐标系内一点,封装x,y和z属性,三者均为Double类型。
import Cocoa
struct markStruct{
var mark1: Int
var mark2: Int
var mark3: Int
init(mark1: Int, mark2: Int, mark3: Int){
self.mark1 = mark1
self.mark2 = mark2
self.mark3 = mark3
}
}
print("优异成绩:")
var marks = markStruct(mark1: 98, mark2: 96, mark3:100)
print(marks.mark1)
print(marks.mark2)
print(marks.mark3)
print("糟糕成绩:")
var fail = markStruct(mark1: 34, mark2: 42, mark3: 13)
print(fail.mark1)
print(fail.mark2)
print(fail.mark3)
以上程序执行输出结果为:
优异成绩:
98
96
100
糟糕成绩:
34
42
13
类
不要求你为自定义类去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类,系统会自动生成面向其它
代码的外部接口
类和结构体对比
相同点
1.定义属性用于存储值
2.定义方法用于提供功能
3.定义附属脚本用于访问值
4.定义构造器用于生成初始化值
5.通过扩展以增加默认实现的功能
6.符合协议以对某类提供标准功能
不同点 类的附加功能
1.继承 允许一个类继承另一个类的特征
2.类型转换 允许一个类在运行时检查和解释一个类实例的类型
3.解构器 允许一个类实例释放任何其所被分配的资源
4.引用计数 允许对一个类多次引用计数
语法
class classname {
Definition 1
Definition 2
……
Definition N
}
作为引用类型访问类属性
import Cocoa
class MarksStruct {
var mark: Int
init(mark: Int) {
self.mark = mark
}
}
class studentMarks {
var mark1 = 300
var mark2 = 400
var mark3 = 900
}
let marks = studentMarks()
print("Mark1 is \(marks.mark1)")
print("Mark2 is \(marks.mark2)")
print("Mark3 is \(marks.mark3)")
Mark1 is 300
Mark2 is 400
Mark3 is 900
恒等运算符
因为类是引用类型 有可能有多个变量或者常量在后台同时引用某一个类实例
恒等运算符是为了判断2个常量或者变量是否引用同一个类实例
WeChatea38c65ccba2c5878f0a398173e57cc9.png
import Cocoa
class SampleClass: Equatable {
let myProperty: String
init(s: String) {
myProperty = s
}
}
func ==(lhs: SampleClass, rhs: SampleClass) -> Bool {
return lhs.myProperty == rhs.myProperty
}
let spClass1 = SampleClass(s: "Hello")
let spClass2 = SampleClass(s: "Hello")
if spClass1 === spClass2 {// false
print("引用相同的类实例 \(spClass1)")
}
if spClass1 !== spClass2 {// true
print("引用不相同的类实例 \(spClass2)")
}
引用不相同的类实例 SampleClass
属性
将值跟特定的类 结构或者枚举关联
属性分类
WeChat11131cf815acef9cd797b0cd747aebb7.png1.存储属性和计算属性通常用于特定类型的实例。
2.属性也可以直接用于类型本身,这种属性称为类型属性。
3.可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己写的存储属性上,
也可以添加到从父类继承的属性上
存储属性
存储属性是存储在特定类或者结构体的实例里面的一个常量或者变量
分别变量存储属性 常量存储属性
可以在定义存储属性的时候指定默认值
可以在构造方法中设置或者修改存储属性的值 甚至修改常量存储属性的值
import Cocoa
struct Number
{
var digits: Int
let pi = 3.1415
}
var n = Number(digits: 12345)
n.digits = 67
print("\(n.digits)")
print("\(n.pi)")
延迟存储属性
延迟存储属性是当第一次被调用的时候才会计算其初始值的属性
在属性声明前使用 lazy 来标示一个延迟存储属性
必须将延迟存储属性声明成变量 因为属性的值在实例构造完成之前无法获取,而常量属性在实例构造方法完成之前必须有初始值。因此无法声明成延迟属性
延迟属性一般用于
1.延迟对象的创建
2.当属性的值依赖于其他未知类
import Cocoa
class sample {
lazy var no = number() // `var` 关键字是必须的
}
class number {
var name = "Runoob Swift 教程"
}
var firstsample = sample()
print(firstsample.no.name)
Runoob Swift 教程
实例化变量
一个类型中属性的全部信息 -- 包括命名 类型和内存管理特征 都在唯一一个地方(类型定义)中定义
计算属性
计算属性不直接存储值,而是通过一个getter方法来获取值 一个可选的setter来间接设置其他属性或者变量的值
import Cocoa
class sample {
var no1 = 0.0, no2 = 0.0
var length = 300.0, breadth = 150.0
var middle: (Double, Double) {
get{
return (length / 2, breadth / 2)
}
set(axis){
no1 = axis.0 - (length / 2)
no2 = axis.1 - (breadth / 2)
}
}
}
var result = sample()
print(result.middle)
result.middle = (0.0, 10.0)
print(result.no1)
print(result.no2)
(150.0, 75.0)
-150.0
-65.0
如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 newValue。
只读计算属性
只有getter没有setter的属性就是只读计算属性
只读计算属性总是返回一个值 可以通过点运算符访问 但是,不能设置新的值
import Cocoa
class film {
var head = ""
var duration = 0.0
var metaInfo: [String:String] {
return [
"head": self.head,
"duration":"\(self.duration)"
]
}
}
var movie = film()
movie.head = "Swift 属性"
movie.duration = 3.09
print(movie.metaInfo["head"]!)
print(movie.metaInfo["duration"]!)
Swift 属性
3.09
必须使用var关键字定义计算属性 包括只读计算属性,因为他们的值不是固定的
let关键字只用来声明常量属性 表示初始化之后再也无法修改的值
属性观察器
属性观察器监视和响应属性的变化 每次属性被设置值的时候都会调用属性观察器 甚至新的值和现在的值相同的时候也不例外
可以为除了延迟存储属性之外的所有存储属性添加属性观察器 也可以通过重载属性的方式为继承的属性(包括存储属性和计
算属性)添加属性观察器
不需要为无法重载的计算属性添加属性观察器,因为可以通过setter直接监控和响应值的变化
可以为属性添加一个或全部的属性观察器
1.willSet 在设置新的值之前调用
2.didSet 在新的值被设置之后立即调用
3. willSet 和 didSet 观察器在属性初始化过程中不会被调用
import Cocoa
class Samplepgm {
var counter: Int = 0{
willSet(newTotal){
print("计数器: \(newTotal)")
}
didSet{
if counter > oldValue {
print("新增数 \(counter - oldValue)")
}
}
}
}
let NewCounter = Samplepgm()
NewCounter.counter = 100
NewCounter.counter = 800
计数器: 100
新增数 100
计数器: 800
新增数 700
全局变量和局部变量
WeChat6c37e79383925660991f64be6967cc72.png类型属性
类型属性是作为类型定义的一部分写在类型最外层的花括号({})内的
使用关键字static来定义值类型的类型属性
使用关键字class来定义类的类型属性
struct Structname {
static var storedTypeProperty = " "
static var computedTypeProperty: Int {
// 这里返回一个 Int 值
}
}
enum Enumname {
static var storedTypeProperty = " "
static var computedTypeProperty: Int {
// 这里返回一个 Int 值
}
}
class Classname {
class var computedTypeProperty: Int {
// 这里返回一个 Int 值
}
}
获取和设置类型属性的值
类型属性是通过类型本身来获取和设置的,不是通过类实例
import Cocoa
struct StudMarks {
static let markCount = 97
static var totalCount = 0
var InternalMarks: Int = 0 {
didSet {
if InternalMarks > StudMarks.markCount {
InternalMarks = StudMarks.markCount
}
if InternalMarks > StudMarks.totalCount {
StudMarks.totalCount = InternalMarks
}
}
}
}
var stud1Mark1 = StudMarks()
var stud1Mark2 = StudMarks()
stud1Mark1.InternalMarks = 98
print(stud1Mark1.InternalMarks)
stud1Mark2.InternalMarks = 87
print(stud1Mark2.InternalMarks)
97
87
方法
方法是与某些特定类型相关联的函数
实例方法
是属于某个特定类 结构体或者枚举类型实例的方法
1.可以访问和修改实例属性
2.提供与实例目的相关的功能
- 实例方法要写在他所属的类型的前后大括号({})之间
- 实例方法能够隐式访问他所属类型的其他实例方法和属性
- 实例方法只能被他所属类型的特定的实例访问
- 实例方法不能脱离于现实的实例而被调用
语法
func funcname(Parameters) -> returntype
{
Statement1
Statement2
……
Statement N
return parameters
}
import Cocoa
class Counter {
var count = 0
func increment() {
count += 1
}
func incrementBy(amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
// 初始计数值是0
let counter = Counter()
// 计数值现在是1
counter.increment()
// 计数值现在是6
counter.incrementBy(amount: 5)
print(counter.count)
// 计数值现在是0
counter.reset()
print(counter.count)
方法的局部参数名称和外部参数名称
默认仅给方法的第一个参数名称一个局部参数名称,默认同时给第二个和后续的参数名称为全局参数名称
是否提供外部名称设置
我们强制在第一个参数添加外部名称,把这个局部名称当做外部名称使用
可以使用_设置第二个以及后续的参数不提供一个外部名称
self属性
类型的实例都有一个隐藏的属性self self完全等同于实例本身
可以在实例的实例方法中使用这个隐藏属性self来引用当前实例
import Cocoa
class calculations {
let a: Int
let b: Int
let res: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
res = a + b
print("Self 内: \(res)")
}
func tot(c: Int) -> Int {
return res - c
}
func result() {
print("结果为: \(tot(c: 20))")
print("结果为: \(tot(c: 50))")
}
}
let pri = calculations(a: 600, b: 300)
let sum = calculations(a: 1200, b: 300)
pri.result()
sum.result()
在实例方法中修改值类型
结构体和枚举是值类型
一般情况下,值类型的属性不能在它的实例方法中被修改
如果要在具体方法中修改结构体或枚举的属性,可以用变异(mutating)这个方法,然后,方法可以从方法内部改变他的属
性,并且,`他做的任何改变在方法结束时还会保留在原始结构中`
方法还可以给他隐含的self属性赋予一个全新的实例,这个新实例在方法结束后将替换原来的实例
import Cocoa
struct area {
var length = 1
var breadth = 1
func area() -> Int {
return length * breadth
}
mutating func scaleBy(res: Int) {
length *= res
breadth *= res
print(length)
print(breadth)
}
}
var val = area(length: 3, breadth: 5)
val.scaleBy(res: 3)
val.scaleBy(res: 30)
val.scaleBy(res: 300)
在可变方法中给self赋值
可变方法能够赋给隐含属性self 一个全新的实例
import Cocoa
struct area {
var length = 1
var breadth = 1
func area() -> Int {
return length * breadth
}
mutating func scaleBy(res: Int) {
self.length *= res
self.breadth *= res
print(length)
print(breadth)
}
}
var val = area(length: 3, breadth: 5)
val.scaleBy(res: 13)
39
65
类型方法
实例方法是被类型的某个实例调用的方法 也可以定义类型本身调用的方法 叫做类型方法
声明结构体和枚举的类型方法 ,需要在func前面加上static 类可能会用class来允许子类重写父类的实现方法
import Cocoa
class Math
{
class func abs(number: Int) -> Int
{
if number < 0
{
return (-number)
}
else
{
return number
}
}
}
struct absno
{
static func abs(number: Int) -> Int
{
if number < 0
{
return (-number)
}
else
{
return number
}
}
}
let no = Math.abs(number: -35)
let num = absno.abs(number: -5)
print(no)
print(num)
下标脚本
可以认为是访问方法 集合和序列的快捷方式 不需要再调用实例的特定的赋值和访问方法
对于同一个目标可以定义多个下标脚本,通过索引值类型的不同来进行重载,而且索引值可以是多个
下标脚本语法及应用
下标脚本允许你通过在实例后面的括号里面传入一个或多个的索引值来对实例进行访问和赋值
定义下标脚本使用subscript关键字,显示声明入参(一个或多个)和返回类型
可以设定为只读和读写
subscript(index: Int) -> Int {
get {
// 用于下标脚本值的声明
}
set(newValue) {
// 执行赋值操作
}
}
import Cocoa
struct subexample {
let decrementer: Int
subscript(index: Int) -> Int {
return decrementer / index
}
}
let division = subexample(decrementer: 100)
print("100 除以 9 等于 \(division[9])")
print("100 除以 2 等于 \(division[2])")
print("100 除以 3 等于 \(division[3])")
print("100 除以 5 等于 \(division[5])")
print("100 除以 7 等于 \(division[7])")
import Cocoa
class daysofaweek {
private var days = ["Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "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])
用法
import Cocoa
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
print(numberOfLegs)
["ant": 6, "bird": 2, "cat": 4, "spider": 8]
下标脚本选项
- 下标脚本允许任意数量的入参索引,并且每个入参类型也没有限制
- 下标脚本的返回值也可以是任意类型
- 下标节本可以使用变量参数和可变参数
- 一个类或结构体可以根据自己需要提供多个下标脚本实现,在定义下标脚本时通过传入参数的类型进行区分,使用下标脚本
的时候会自动匹配合适的下标脚本实现运行 这就是`下标脚本的重载`
import Cocoa
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
}
}
}
// 创建了一个新的 3 行 3 列的Matrix实例
var mat = Matrix(rows: 3, columns: 3)
// 通过下标脚本设置值
mat[0,0] = 1.0
mat[0,1] = 2.0
mat[1,0] = 3.0
mat[1,1] = 5.0
// 通过下标脚本获取值
print("\(mat[0,0])")
print("\(mat[0,1])")
print("\(mat[1,0])")
print("\(mat[1,1])")
1.0
2.0
3.0
5.0
继承
一个类获取了另一个类的方法和属性
当一个类继承其它类时,继承类叫子类,被继承类叫超类(或父类)
类可以调用和访问超类的方法 属性和下标脚本 并且可以重写他们
可以为继承来的属性添加属性观察器
基类
没有继承其他的类是基类
class StudDetails {
var stname: String!
var mark1: Int!
var mark2: Int!
var mark3: Int!
init(stname: String, mark1: Int, mark2: Int, mark3: Int) {
self.stname = stname
self.mark1 = mark1
self.mark2 = mark2
self.mark3 = mark3
}
}
let stname = "swift"
let mark1 = 98
let mark2 = 89
let mark3 = 76
let sds = StudDetails(stname:stname, mark1:mark1, mark2:mark2, mark3:mark3);
print(sds.stname)
print(sds.mark1)
print(sds.mark2)
print(sds.mark3)
子类
在一个已有类的基础上创建一个新类
为了指明某个类的超类,将超类名写在子类名的后面,用冒号(:)分隔
class SomeClass: SomeSuperclass {
// 类的定义
}
class StudDetails
{
var mark1: Int;
var mark2: Int;
init(stm1:Int, results stm2:Int)
{
mark1 = stm1;
mark2 = stm2;
}
func show()
{
print("Mark1:\(self.mark1), Mark2:\(self.mark2)")
}
}
class Tom : StudDetails
{
init()
{
super.init(stm1: 93, results: 89)
}
}
let tom = Tom()
tom.show()
Mark1:93, Mark2:89
重写
子类可以通过继承来的实例方法 类方法 实例属性或下标脚本 来定制自己的功能,叫做重写
使用override关键字实现
访问超类的方法、属性及下标脚本.png
重写方法和属性
class SuperClass {
func show() {
print("这是超类 SuperClass")
}
}
class SubClass: SuperClass {
override func show() {
print("这是子类 SubClass")
}
}
let superClass = SuperClass()
superClass.show()
let subClass = SubClass()
subClass.show()
可以提供定制的getter setter重写任意继承来的属性 无论是存储属性还是计算属性
重写属性的时候必须带名字和类型
1.如果在重写属性中提供了setter方法 那么,一定要提供getter方法
2.如果不想在重写版本中的getter方法中修改继承来的属性,可以直接调用super.somproperty来返回继承来的值 其中someProperty是你要重写的属性的名字
var radius = 12.5
var area: String {
return "矩形半径 \(radius) "
}
}
// 继承超类 Circle
class Rectangle: Circle {
var print = 7
override var area: String {
return super.area + " ,但现在被重写为 \(print)"
}
}
let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("Radius \(rect.area)")
Radius 矩形半径 25.0 ,但现在被重写为 3
重写属性观察器
可以为一个继承来的属性添加属性观察器 当继承来的属性值发生改变时,你就会监测到
不可以为继承来的常量存储性属性和只读计算型属性添加属性观察器
class Circle {
var radius = 12.5
var area: String {
return "矩形半径为 \(radius) "
}
}
class Rectangle: Circle {
var print = 7
override var area: String {
return super.area + " ,但现在被重写为 \(print)"
}
}
let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("半径: \(rect.area)")
class Square: Rectangle {
override var radius: Double {
didSet {
print = Int(radius/5.0)+1
}
}
}
let sq = Square()
sq.radius = 100.0
print("半径: \(sq.area)")
半径: 矩形半径为 25.0 ,但现在被重写为 3
半径: 矩形半径为 100.0 ,但现在被重写为 21
防止重写
使用final 关键字防止重写
可以通过在关键字class前添加final特性(final class)来将整个类标记为 final 的
这样的类是不可被继承的,否则会报编译错误。
final class Circle {
final var radius = 12.5
var area: String {
return "矩形半径为 \(radius) "
}
}
class Rectangle: Circle {
var print = 7
override var area: String {
return super.area + " ,但现在被重写为 \(print)"
}
}
let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("半径: \(rect.area)")
class Square: Rectangle {
override var radius: Double {
didSet {
print = Int(radius/5.0)+1
}
}
}
let sq = Square()
sq.radius = 100.0
print("半径: \(sq.area)")
构造过程
是为了使用某个类 结构体或者枚举类型的实例而进行的准备过程 这个过程包含了为实例中的每个属性设置初始值和为其执
行必要的准备和初始化任务。
无需返回值 主要任务是保证新实例在第一次使用前完成正确的初始化
类实例可以通过定义析构器在类实例释放之前执行清理内存的工作
存储性属性的初始赋值
- 类和结构体在实例化的时候,必须为所有的存储性属性赋予合适的初始值
- 存储属性在构造器里面赋值的时候,他们的值是直接赋值的,不会触发任何属性观察器
赋值流程
1.创建初始值
2.在属性定义时指定默认值
3.初始化实例 并调用init()方法
构造器
构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名
struct rectangle {
var length: Double
var breadth: Double
init() {
length = 6
breadth = 12
}
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")
默认属性值
我们可以在构造器中为存储型属性设置初始值;同样,也可以在属性声明时为其设置默认值。
使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型。
struct rectangle {
// 设置默认值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")
构造参数
可以在定义构造器 init() 时提供构造参数
struct Rectangle {
var length: Double
var breadth: Double
var area: Double
init(fromLength length: Double, fromBreadth breadth: Double) {
self.length = length
self.breadth = breadth
area = length * breadth
}
init(fromLeng leng: Double, fromBread bread: Double) {
self.length = leng
self.breadth = bread
area = leng * bread
}
}
let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面积为: \(ar.area)")
let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面积为: \(are.area)")
内部和外部参数名
在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
print("red 值为: \(magenta.red)")
print("green 值为: \(magenta.green)")
print("blue 值为: \(magenta.blue)")
// 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let halfGray = Color(white: 0.5)
print("red 值为: \(halfGray.red)")
print("green 值为: \(halfGray.green)")
print("blue 值为: \(halfGray.blue)")
red 值为: 1.0
green 值为: 0.0
blue 值为: 1.0
red 值为: 0.5
green 值为: 0.5
blue 值为: 0.5
没有外部名称参数
struct Rectangle {
var length: Double
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
//不提供外部名字
init(_ area: Double) {
length = area
}
}
// 调用不提供外部名字
let rectarea = Rectangle(180.0)
print("面积为: \(rectarea.length)")
// 调用不提供外部名字
let rearea = Rectangle(370.0)
print("面积为: \(rearea.length)")
// 调用不提供外部名字
let recarea = Rectangle(110.0)
print("面积为: \(recarea.length)")
可选属性类型
如果你定制的类型包含一个逻辑上允许取值为空的存储型属性,你都需要将它定义为可选类型optional type
(可选属性类型)。
当存储属性声明为可选时,将自动初始化为空 nil。
struct Rectangle {
var length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")
let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")
构造过程中修改常量属性
只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值
对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
struct Rectangle {
let length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)")
let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)")
let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")
默认构造器
默认构造器将简单的创建一个所有属性值都设置为默认值的实例
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")
如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器
值类型的构造器代理
构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
// origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 结构体初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 结构体初始值: \(basicRect.origin.x, basicRect.origin.y) ")
// 将origin和size的参数值赋给对应的存储型属性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
print("Size 结构体初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 结构体初始值: \(originRect.origin.x, originRect.origin.y) ")
//先通过center和size的值计算出origin的坐标。
//然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
print("Size 结构体初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 结构体初始值: \(centerRect.origin.x, centerRect.origin.y) ")
Size 结构体初始值: (0.0, 0.0)
Rect 结构体初始值: (0.0, 0.0)
Size 结构体初始值: (5.0, 5.0)
Rect 结构体初始值: (2.0, 2.0)
Size 结构体初始值: (3.0, 3.0)
Rect 结构体初始值: (2.5, 2.5)
构造器代理规则
WeChate4a743dabd0b17b3e0125146ca19eea5.png类的继承和构造过程 WeChatfd8225807fa804b340539851bc654468.png
指定构造器
class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int // 新的子类存储变量
init(no1 : Int, no2 : Int) {
self.no2 = no2 // 初始化
super.init(no1:no1) // 初始化超类
}
}
let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)
print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")
便利构造器
class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int
init(no1 : Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 便利方法只需要一个参数
override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1: 20)
let res2 = subClass(no1: 30, no2: 50)
print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")
构造器的继承和重载
- 子类不会默认继承父类的构造器
- 父类的构造器仅在确定和安全的情况下被继承
- 当你重写一个父类指定构造器时,你需要写override修饰符
class SuperClass {
var corners = 4
var description: String {
return "\(corners) 边"
}
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)")
class SubClass: SuperClass {
override init() { //重载构造器
super.init()
corners = 5
}
}
let subClass = SubClass()
print("五角型: \(subClass.description)")
指定构造器和便利构造器实例
class MainClass {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[匿名]")
}
}
let main = MainClass(name: "Runoob")
print("MainClass 名字为: \(main.name)")
let main2 = MainClass()
print("没有对应名字: \(main2.name)")
class SubClass: MainClass {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, count: 1)
}
}
let sub = SubClass(name: "Runoob")
print("MainClass 名字为: \(sub.name)")
let sub2 = SubClass(name: "Runoob", count: 3)
print("count 变量: \(sub2.count)")
类的可失败构造器
如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器。
变量初始化失败可能的原因有:
1.传入无效的参数值
2.缺少某种所需的外部资源
3.没有满足特定条件
语法
其语法为在init关键字后面加添问号(init?)
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
//通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功
// someCreature 的类型是 Animal? 而不是 Animal
let someCreature = Animal(species: "长颈鹿")
// 打印 "动物初始化为长颈鹿"
if let giraffe = someCreature {
print("动物初始化为\(giraffe.species)")
动物初始化为长颈鹿
枚举类型的可失败构造器
可以通过构造一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员
enum TemperatureUnit {
// 开尔文,摄氏,华氏
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("这是一个已定义的温度单位,所以初始化成功。")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("这不是一个已定义的温度单位,所以初始化失败。")
}
这是一个已定义的温度单位,所以初始化成功。
这不是一个已定义的温度单位,所以初始化失败。
类的可失败构造器
值类型(结构体或枚举)的可失败构造器,对于何时何地触发失败这个行为没有任何限制,但是,类的可失败构造器,只能在所有的类属性初始化完成之后和所有类之间的构造器之间的代理调用发生完成之后才会触发失败行为
class StudRecord {
let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = StudRecord(studname: "失败构造器") {
print("模块为 \(stname.studname)")
}
模块为 失败构造器
覆盖一个可失败构造器
- 可以用子类的可失败构造器覆盖基类的可失败构造器。
- 可以用子类的非可失败构造器覆盖一个基类的可失败构造器。
- 可以用一个非可失败构造器覆盖一个可失败构造器,但反过来却行不通。
- 一个非可失败的构造器永远也不能代理调用一个可失败构造器。
class Planet {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[No Planets]")
}
}
let plName = Planet(name: "Mercury")
print("行星的名字是: \(plName.name)")
let noplName = Planet()
print("没有这个名字的行星: \(noplName.name)")
class planets: Planet {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, count: 1)
}
}
行星的名字是: Mercury
没有这个名字的行星: [No Planets]
可失败构造器 init!
struct StudRecord {
let stname: String
init!(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
}
let stmark = StudRecord(stname: "Runoob")
if let name = stmark {
print("指定了学生名")
}
let blankname = StudRecord(stname: "")
if blankname == nil {
print("学生名为空")
}
指定了学生名
学生名为空
析构过程
在一个类的实例被释放之前,析构函数被立即调用。用关键字deinit来标示析构函数,类似于初始化函数用init来标示。析构函数只适用于类类型
析构过程原理
Swift 会自动释放不再需要的实例以释放资源。
Swift 通过自动引用计数(ARC)处理实例的内存管理。
通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。
语法
每个类最多只有一个析构函数 析构函数不带任何参数
deinit {
// 执行析构过程
}
var counter = 0; // 引用计数器
class BaseClass {
init() {
counter += 1;
}
deinit {
counter -= 1;
}
}
var show: BaseClass? = BaseClass()
print(counter)
show = nil
print(counter)
1
0
var counter = 0; // 引用计数器
class BaseClass {
init() {
counter += 1;
}
deinit {
counter -= 1;
}
}
var show: BaseClass? = BaseClass()
print(counter)
print(counter)
1
1
可选链
可选链是一种可以请求和调用属性 方法和子脚本的过程,用于请求和调用的目标可能为nil
可选链返回2个值
1.如果目标有值,就会调用成功,就会返回该值
2.如果目标为nil 调用就会返回nil
多次请求或者调用可以链接成一个链,如果其中一个节点为nil,就会导致整条链失效
可选链可替代强制解析
通过在属性 方法或者下标脚本的可选值后面放一个?,就可以定义成一个可选链
WeChat8ba6669b95d00bb2e02efb37a822c382.png
使用感叹号(!)可选链实例
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
//将导致运行时错误
let roomCount = john.residence!.numberOfRooms
fatal error: unexpectedly found nil while unwrapping an Optional value
使用问号(?)可选链实例
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
// 链接可选residence?属性,如果residence存在则取回numberOfRooms的值
if let roomCount = john.residence?.numberOfRooms {
print("John 的房间号为 \(roomCount)。")
} else {
print("不能查看房间号")
}
不能查看房间号
为可选链定义模型类
可以用可选链来多层调用属性 方法和下标脚本,可以利用他们之间复杂模型获取更底层的属性,并检查是否可以成功获取此
类的底层属性
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
通过可选链调用方法
可以使用可选链来调用可选值的方法并检查方法是否调用成功,即使这个方法没有返回成功,你也可以使用可选链来达成这一目的
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if ((john.residence?.printNumberOfRooms()) != nil) {
print("输出房间号")
} else {
print("无法输出房间号")
}
无法输出房间号
使用可选链调用下标脚本
可以使用可选链尝试从下标脚本中获取值并检查下表脚本是否调用成功,但是,不能通过可选链来设置下标脚本
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if let firstRoomName = john.residence?[0].name {
print("第一个房间名 \(firstRoomName).")
} else {
print("无法检索到房间")
}
无法检索到房间
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence!.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
print("John 所在的街道是 \(johnsStreet)。")
} else {
print("无法检索到地址。 ")
}
John 所在的街道是 Laurel Street。
通过可选链接调用来访问下标
通过可选链接调用,我们可以用下标来对可选值进行读取或写入 并且判断下标调用是否成功
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "客厅"))
johnsHouse.rooms.append(Room(name: "厨房"))
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
print("第一个房间名为\(firstRoomName)")
} else {
print("无法检索到房间")
}
第一个房间名为客厅
访问可选类型的下标
如果下标返回可空类型值,比如Swift中Dictionary的key下标。可以在下标的闭合括号后面放一个问号来链接下标的可
空返回值
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0]++
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
连接多层链接
可以将多层可选链连接在一起,可以掘取模型内更下层的属性方法和下标脚本。然而多层可选链不能再添加比已经返回的可选值更多的层
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if let johnsStreet = john.residence?.address?.street {
print("John 的地址为 \(johnsStreet).")
} else {
print("不能检索地址")
}
不能检索地址
对返回可选值的函数进行链接
可以通过可选链接调用可以返回可空值的方法,并且可以继续对可选值进行链接
class Person {
var residence: Residence?
}
// 定义了一个变量 rooms,它被初始化为一个Room[]类型的空数组
class Residence {
var rooms = [Room]()
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
return rooms[i]
}
func printNumberOfRooms() {
print("房间号为 \(numberOfRooms)")
}
var address: Address?
}
// Room 定义一个name属性和一个设定room名的初始化器
class Room {
let name: String
init(name: String) { self.name = name }
}
// 模型中的最终类叫做Address
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if (buildingName != nil) {
return buildingName
} else if (buildingNumber != nil) {
return buildingNumber
} else {
return nil
}
}
}
let john = Person()
if john.residence?.printNumberOfRooms() != nil {
print("指定了房间号)")
} else {
print("未指定房间号")
}
未指定房间号
自动引用计数ARC
跟踪和管理应用程序的内存
功能
- 当每次使用init方法创建一个类的新的实例的时候,ARC会分配一大块内存来存储实例的信息
- 内存中会包含实例的类型信息 和这个实例所包含的所有属性的值
- 当实例不再被使用的时候,ARC会释放实例所占有的内存,并且让释放的内存能挪作他用
- 为了确保使用中的实例不会被销毁,ARC 会跟踪和计算每一个实例正在被多少属性,常量和变量所引用
- 实例赋值给属性 常量或者变量 他们都会创建此实例的强引用 只要强引用还在,实例是不允许被销毁的
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) 开始初始化")
}
deinit {
print("\(name) 被析构")
}
}
// 值会被自动初始化为nil,目前还不会引用到Person类的实例
var reference1: Person?
var reference2: Person?
var reference3: Person?
// 创建Person类的新实例
reference1 = Person(name: "Runoob")
//赋值给其他两个变量,该实例又会多出两个强引用
reference2 = reference1
reference3 = reference1
//断开第一个强引用
reference1 = nil
//断开第二个强引用
reference2 = nil
//断开第三个强引用,并调用析构函数
reference3 = nil
Runoob 开始初始化
Runoob 被析构
类实例之间的循环强引用
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) 被析构") }
}
class Apartment {
let number: Int
init(number: Int) { self.number = number }
var tenant: Person?
deinit { print("Apartment #\(number) 被析构") }
}
// 两个变量都被初始化为nil
var runoob: Person?
var number73: Apartment?
// 赋值
runoob = Person(name: "Runoob")
number73 = Apartment(number: 73)
// 意感叹号是用来展开和访问可选变量 runoob 和 number73 中的实例
// 循环强引用被创建
runoob!.apartment = number73
number73!.tenant = runoob
// 断开 runoob 和 number73 变量所持有的强引用时,引用计数并不会降为 0,实例也不会被 ARC 销毁
// 注意,当你把这两个变量设为nil时,没有任何一个析构函数被调用。
// 强引用循环阻止了Person和Apartment类实例的销毁,并在你的应用程序中造成了内存泄漏
runoob = nil
number73 = nil
解决实例之间的循环强引用
1.弱引用
2.无主引用
弱引用和无主引用允许循环引用中的一个实例引用另一个实例不保持强引用,这样,实例能够相互引用而不产生循环强引用
对于生命周期中会变为nil的实例使用弱引用。相反的,对于初始化赋值后再也不会被赋值为nil的实例,使用无主引用
弱引用
class Module {
let name: String
init(name: String) { self.name = name }
var sub: SubModule?
deinit { print("\(name) 主模块") }
}
class SubModule {
let number: Int
init(number: Int) { self.number = number }
weak var topic: Module?
deinit { print("子模块 topic 数为 \(number)") }
}
var toc: Module?
var list: SubModule?
toc = Module(name: "ARC")
list = SubModule(number: 4)
toc!.sub = list
list!.topic = toc
toc = nil
list = nil
ARC 主模块
子模块 topic 数为 4
无主引用
class Student {
let name: String
var section: Marks?
init(name: String) {
self.name = name
}
deinit { print("\(name)") }
}
class Marks {
let marks: Int
unowned let stname: Student
init(marks: Int, stname: Student) {
self.marks = marks
self.stname = stname
}
deinit { print("学生的分数为 \(marks)") }
}
var module: Student?
module = Student(name: "ARC")
module!.section = Marks(marks: 98, stname: module!)
module = nil
ARC
学生的分数为 98
闭包引起的循环强引用
当将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了实例。会发生循环强引用
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
// 创建实例并打印信息
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
HTMLElement 类产生了类实例和 asHTML 默认值的闭包之间的循环强引用。
实例的 asHTML 属性持有闭包的强引用。但是,闭包在其闭包体内使用了self(引用了self.name和self.text),因此闭包捕获了self,这意味着闭包又反过来持有了HTMLElement实例的强引用。这样两个对象就产生了循环强引用。
解决闭包引起的循环强引用:在定义闭包时同时定义捕获列表作为闭包的一部分,通过这种方式可以解决闭包和类实例之间的循环强引用。
弱引用和无主引用
当闭包和捕获的实例总是互相引用时并且总是同时销毁时,将闭包内的捕获定义为无主引用。
相反的,当捕获引用有时可能会是nil时,将闭包内的捕获定义为弱引用。
如果捕获的引用绝对不会置为nil,应该用无主引用,而不是弱引用。
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = {
` [unowned self] in`
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) 被析构")
}
}
//创建并打印HTMLElement实例
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
// HTMLElement实例将会被销毁,并能看到它的析构函数打印出的消息
paragraph = nil
<p>hello, world</p>
p 被析构
类型转换
可以判断实例的类型 也可以用于检测实例类型是否属于其父类或者子类的实例
使用as和is实现类型转换 其中,is用于检测值的类型 as用于类型转换
类型转换可以用来检测一个类是否实现了某个协议
class Subjects {
var physics: String
init(physics: String) {
self.physics = physics
}
}
class Chemistry: Subjects {
var equations: String
init(physics: String, equations: String) {
self.equations = equations
super.init(physics: physics)
}
}
class Maths: Subjects {
var formulae: String
init(physics: String, formulae: String) {
self.formulae = formulae
super.init(physics: physics)
}
}
let sa = [
Chemistry(physics: "固体物理", equations: "赫兹"),
Maths(physics: "流体动力学", formulae: "千兆赫")]
let samplechem = Chemistry(physics: "固体物理", equations: "赫兹")
print("实例物理学是: \(samplechem.physics)")
print("实例方程式: \(samplechem.equations)")
let samplemaths = Maths(physics: "流体动力学", formulae: "千兆赫")
print("实例物理学是: \(samplemaths.physics)")
print("实例公式是: \(samplemaths.formulae)")
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
检查类型
类型检查用于检测实例类型是否是特殊的实例类型
检查特定类实例的类型并且转换这个类实例的类型成为这个层次结构中的其他类型
类型检查用is关键字
操作符 is 来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false
class Subjects {
var physics: String
init(physics: String) {
self.physics = physics
}
}
class Chemistry: Subjects {
var equations: String
init(physics: String, equations: String) {
self.equations = equations
super.init(physics: physics)
}
}
class Maths: Subjects {
var formulae: String
init(physics: String, formulae: String) {
self.formulae = formulae
super.init(physics: physics)
}
}
let sa = [
Chemistry(physics: "固体物理", equations: "赫兹"),
Maths(physics: "流体动力学", formulae: "千兆赫"),
Chemistry(physics: "热物理学", equations: "分贝"),
Maths(physics: "天体物理学", formulae: "兆赫"),
Maths(physics: "微分方程", formulae: "余弦级数")]
let samplechem = Chemistry(physics: "固体物理", equations: "赫兹")
print("实例物理学是: \(samplechem.physics)")
print("实例方程式: \(samplechem.equations)")
let samplemaths = Maths(physics: "流体动力学", formulae: "千兆赫")
print("实例物理学是: \(samplemaths.physics)")
print("实例公式是: \(samplemaths.formulae)")
var chemCount = 0
var mathsCount = 0
for item in sa {
// 如果是一个 Chemistry 类型的实例,返回 true,相反返回 false。
if item is Chemistry {
++chemCount
} else if item is Maths {
++mathsCount
}
}
print("化学科目包含 \(chemCount) 个主题,数学包含 \(mathsCount) 个主题")
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
化学科目包含 2 个主题,数学包含 3 个主题
向下转型
向下转型,用类型转换操作符(as? 或 as!)
- 当不确定向下转型可以成功时,用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值
(optional value),并且若下转是不可能的,可选值将是 nil。
- 当可以确定向下转型一定会成功时,才使用强制形式(as!)。当试图向下转型为一个不正确的类型时,强制形式的类型转
换会触发一个运行时错误
class Subjects {
var physics: String
init(physics: String) {
self.physics = physics
}
}
class Chemistry: Subjects {
var equations: String
init(physics: String, equations: String) {
self.equations = equations
super.init(physics: physics)
}
}
class Maths: Subjects {
var formulae: String
init(physics: String, formulae: String) {
self.formulae = formulae
super.init(physics: physics)
}
}
let sa = [
Chemistry(physics: "固体物理", equations: "赫兹"),
Maths(physics: "流体动力学", formulae: "千兆赫"),
Chemistry(physics: "热物理学", equations: "分贝"),
Maths(physics: "天体物理学", formulae: "兆赫"),
Maths(physics: "微分方程", formulae: "余弦级数")]
let samplechem = Chemistry(physics: "固体物理", equations: "赫兹")
print("实例物理学是: \(samplechem.physics)")
print("实例方程式: \(samplechem.equations)")
let samplemaths = Maths(physics: "流体动力学", formulae: "千兆赫")
print("实例物理学是: \(samplemaths.physics)")
print("实例公式是: \(samplemaths.formulae)")
var chemCount = 0
var mathsCount = 0
for item in sa {
// 类型转换的条件形式
if let show = item as? Chemistry {
print("化学主题是: '\(show.physics)', \(show.equations)")
// 强制形式
} else if let example = item as? Maths {
print("数学主题是: '\(example.physics)', \(example.formulae)")
}
}
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
化学主题是: '固体物理', 赫兹
数学主题是: '流体动力学', 千兆赫
化学主题是: '热物理学', 分贝
数学主题是: '天体物理学', 兆赫
数学主题是: '微分方程', 余弦级数
Any和AnyObject的类型转换
为不确定类型提供了2种特殊类型别名
1.AnyObject可以代表任何class类型的实例
2.Any可以表示任何类型 也包括方法类型(function types)
只有当你明确的需要他的行为和功能的时候你才能使用Any和AnyObject 在你的代码里面,用你期望的明确的类型总是更好的
Any实例
class Subjects {
var physics: String
init(physics: String) {
self.physics = physics
}
}
class Chemistry: Subjects {
var equations: String
init(physics: String, equations: String) {
self.equations = equations
super.init(physics: physics)
}
}
class Maths: Subjects {
var formulae: String
init(physics: String, formulae: String) {
self.formulae = formulae
super.init(physics: physics)
}
}
let sa = [
Chemistry(physics: "固体物理", equations: "赫兹"),
Maths(physics: "流体动力学", formulae: "千兆赫"),
Chemistry(physics: "热物理学", equations: "分贝"),
Maths(physics: "天体物理学", formulae: "兆赫"),
Maths(physics: "微分方程", formulae: "余弦级数")]
let samplechem = Chemistry(physics: "固体物理", equations: "赫兹")
print("实例物理学是: \(samplechem.physics)")
print("实例方程式: \(samplechem.equations)")
let samplemaths = Maths(physics: "流体动力学", formulae: "千兆赫")
print("实例物理学是: \(samplemaths.physics)")
print("实例公式是: \(samplemaths.formulae)")
var chemCount = 0
var mathsCount = 0
for item in sa {
// 类型转换的条件形式
if let show = item as? Chemistry {
print("化学主题是: '\(show.physics)', \(show.equations)")
// 强制形式
} else if let example = item as? Maths {
print("数学主题是: '\(example.physics)', \(example.formulae)")
}
}
// 可以存储Any类型的数组 exampleany
var exampleany = [Any]()
exampleany.append(12)
exampleany.append(3.14159)
exampleany.append("Any 实例")
exampleany.append(Chemistry(physics: "固体物理", equations: "兆赫"))
for item2 in exampleany {
switch item2 {
case let someInt as Int:
print("整型值为 \(someInt)")
case let someDouble as Double where someDouble > 0:
print("Pi 值为 \(someDouble)")
case let someString as String:
print("\(someString)")
case let phy as Chemistry:
print("主题 '\(phy.physics)', \(phy.equations)")
default:
print("None")
}
}
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
化学主题是: '固体物理', 赫兹
数学主题是: '流体动力学', 千兆赫
化学主题是: '热物理学', 分贝
数学主题是: '天体物理学', 兆赫
数学主题是: '微分方程', 余弦级数
整型值为 12
Pi 值为 3.14159
Any 实例
主题 '固体物理', 兆赫
AnyObject 实例
class Subjects {
var physics: String
init(physics: String) {
self.physics = physics
}
}
class Chemistry: Subjects {
var equations: String
init(physics: String, equations: String) {
self.equations = equations
super.init(physics: physics)
}
}
class Maths: Subjects {
var formulae: String
init(physics: String, formulae: String) {
self.formulae = formulae
super.init(physics: physics)
}
}
// [AnyObject] 类型的数组
let saprint: [AnyObject] = [
Chemistry(physics: "固体物理", equations: "赫兹"),
Maths(physics: "流体动力学", formulae: "千兆赫"),
Chemistry(physics: "热物理学", equations: "分贝"),
Maths(physics: "天体物理学", formulae: "兆赫"),
Maths(physics: "微分方程", formulae: "余弦级数")]
let samplechem = Chemistry(physics: "固体物理", equations: "赫兹")
print("实例物理学是: \(samplechem.physics)")
print("实例方程式: \(samplechem.equations)")
let samplemaths = Maths(physics: "流体动力学", formulae: "千兆赫")
print("实例物理学是: \(samplemaths.physics)")
print("实例公式是: \(samplemaths.formulae)")
var chemCount = 0
var mathsCount = 0
for item in saprint {
// 类型转换的条件形式
if let show = item as? Chemistry {
print("化学主题是: '\(show.physics)', \(show.equations)")
// 强制形式
} else if let example = item as? Maths {
print("数学主题是: '\(example.physics)', \(example.formulae)")
}
}
var exampleany = [Any]()
exampleany.append(12)
exampleany.append(3.14159)
exampleany.append("Any 实例")
exampleany.append(Chemistry(physics: "固体物理", equations: "兆赫"))
for item2 in exampleany {
switch item2 {
case let someInt as Int:
print("整型值为 \(someInt)")
case let someDouble as Double where someDouble > 0:
print("Pi 值为 \(someDouble)")
case let someString as String:
print("\(someString)")
case let phy as Chemistry:
print("主题 '\(phy.physics)', \(phy.equations)")
default:
print("None")
}
}
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
化学主题是: '固体物理', 赫兹
数学主题是: '流体动力学', 千兆赫
化学主题是: '热物理学', 分贝
数学主题是: '天体物理学', 兆赫
数学主题是: '微分方程', 余弦级数
整型值为 12
Pi 值为 3.14159
Any 实例
主题 '固体物理', 兆赫
扩展
是向已有的类 结构体或是枚举类型添加新的功能
扩展可以对一个类型添加新的功能,但是,不能重写已有功能
1.添加计算型属性和计算型静态属性
2.定义实例方法和类型方法
3.提供新的构造器
4.定义下标
5.定义和使用新的嵌套类型
6.使一个已有类型符合某个协议
语法
extension SomeType {
// 加到SomeType的新功能写到这里
}
一个扩展可以扩展一个已有类型 使其可以适配一个或者多个协议
extension SomeType: SomeProtocol, AnotherProctocol {
// 协议实现写到这里
}
计算型属性
扩展可以向已有类型添加计算型实例属性和计算型类型属性
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)")
加法运算后的值:103
减法运算后的值:110
乘法运算后的值:390
除法运算后的值: 11
混合运算结果:154
构造器
扩展可以向已有类添加新的构造器
可以扩展其他类型 将定制类型作为构造器参数 或者提供该类型的原始实现中没有包含的额外初始化选项
扩展可以添加新的便利构造器,但是,不能添加新的指定构造器 和析构函数
struct sum {
var num1 = 100, num2 = 200
}
struct diff {
var no1 = 200, no2 = 100
}
struct mult {
var a = sum()
var b = diff()
}
extension mult {
init(x: sum, y: diff) {
_ = x.num1 + x.num2
_ = y.no1 + y.no2
}
}
let a = sum(num1: 100, num2: 200)
let b = diff(no1: 200, no2: 100)
let getMult = mult(x: a, y: b)
print("getMult sum\(getMult.a.num1, getMult.a.num2)")
print("getMult diff\(getMult.b.no1, getMult.b.no2)")
getMult sum(100, 200)
getMult diff(200, 100)
方法
扩展可以向已有类型添加新的实例方法和类型方法。
extension Int {
func topics(summation: () -> ()) {
for _ in 0..<self {
summation()
}
}
}
4.topics({
print("扩展模块内")
})
3.topics({
print("内型转换模块内")
})
扩展模块内
扩展模块内
扩展模块内
扩展模块内
内型转换模块内
内型转换模块内
内型转换模块内
可变实例方法
通过扩展实现的实例方法可以修改该实例本身
结构体或者枚举类型中修改self或者其属性的方法必须将该实例方法标注为mutating
extension Double {
mutating func square() {
let pi = 3.1415
self = pi * self * self
}
}
var Trial1 = 3.3
Trial1.square()
print("圆的面积为: \(Trial1)")
var Trial2 = 5.8
Trial2.square()
print("圆的面积为: \(Trial2)")
var Trial3 = 120.3
Trial3.square()
print("圆的面积为: \(Trial3)")
圆的面积为: 34.210935
圆的面积为: 105.68006
圆的面积为: 45464.070735
下标
可以向一个已有类型添加新的下标
extension Int {
subscript(var multtable: Int) -> Int {
var no1 = 1
while multtable > 0 {
no1 *= 10
--multtable
}
return (self / no1) % 10
}
}
print(12[0])
print(7869[1])
print(786543[2])
2
6
5
嵌套类型
扩展可以向已有的类、结构体和枚举添加新的嵌套类型
extension Int {
enum calc
{
case add
case sub
case mult
case div
case anything
}
var print: calc {
switch self
{
case 0:
return .add
case 1:
return .sub
case 2:
return .mult
case 3:
return .div
default:
return .anything
}
}
}
func result(numb: [Int]) {
for i in numb {
switch i.print {
case .add:
print(" 10 ")
case .sub:
print(" 20 ")
case .mult:
print(" 30 ")
case .div:
print(" 40 ")
default:
print(" 50 ")
}
}
}
result([0, 1, 2, 3, 4, 7])
10
20
30
40
50
50
协议
协议规定了需要实现某一特定功能所需要的方法和属性
任意能够满足这个协议的类型被称为遵循这个协议
类 结构体或者枚举都能遵循协议,并提供具体方法来完成协议定义的方法和功能
语法
protocol SomeProtocol {
// 协议内容
}
要使类遵循某个协议,需要在类型名称后加上协议名称,中间以冒号:分隔,作为类型定义的一部分。遵循多个协议时,各协
议之间用逗号,分隔。
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 结构体内容
}
如果类在遵循协议的同时拥有父类,应该将父类名放在协议名之前,以逗号分隔
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 类的内容
}
对属性的规定
协议用于指定特定的实例属性或者类属性,而不用指定是存储性属性或者计算型属性 还必须指明是只读的或者可读可写的
协议中的通常用var来声明变量属性,在类型声明后加上{ set get }来表示属性是可读可写的,只读属性则用{ get }
来表示
protocol classa {
var marks: Int { get set }
var result: Bool { get }
func attendance() -> String
func markssecured() -> String
}
protocol classb: classa {
var present: Bool { get set }
var subject: String { get set }
var stname: String { get set }
}
class classc: classb {
var marks = 96
let result = true
var present = false
var subject = "Swift 协议"
var stname = "Protocols"
func attendance() -> String {
return "The \(stname) has secured 99% attendance"
}
func markssecured() -> String {
return "\(stname) has scored \(marks)"
}
}
let studdet = classc()
studdet.stname = "Swift"
studdet.marks = 98
studdet.markssecured()
print(studdet.marks)
print(studdet.result)
print(studdet.present)
print(studdet.subject)
print(studdet.stname)
98
true
false
Swift 协议
Swift
对 Mutating 方法的规定
值类型(结构体 枚举类型)的实例方法中,将Mutating关键字作为函数的前缀,放在func前面,表明可以在这个方面里面
修改他所属的实例以及实例所属地属性的值
protocol daysofaweek {
mutating func show()
}
enum days: daysofaweek {
case sun, mon, tue, wed, thurs, fri, sat
mutating func show() {
switch self {
case .sun:
self = .sun
print("Sunday")
case .mon:
self = .mon
print("Monday")
case .tue:
self = .tue
print("Tuesday")
case .wed:
self = .wed
print("Wednesday")
case .thurs:
self = .thurs
print("Wednesday")
case .fri:
self = .fri
print("Wednesday")
case .sat:
self = .sat
print("Saturday")
default:
print("NO Such Day")
}
}
}
var res = days.wed
res.show()
Wednesday
对构造器的规定
协议要求他的遵循者实现指定的构造器
协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体
protocol SomeProtocol {
init(someParameter: Int)
}
协议构造器规定在类中的实现
遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器,必须给构造器实现标上"required"修饰符
class SomeClass: SomeProtocol {
required init(someParameter: Int) {
// 构造器实现
}
}
protocol tcpprotocol {
init(aprot: Int)
}
class tcpClass: tcpprotocol {
required init(aprot: Int) {
}
}
使用required 关键字可以保证,所有的遵循该协议的子类 同样能为构造器规定实现提供一个显示的实现或者继承实现
子类重写了父类的指定构造器,并且该构造器遵循了某个协议的规定,那么该构造器的实现需要同时标记required 和 override 修饰符
protocol tcpprotocol {
init(no1: Int)
}
class mainClass {
var no1: Int // 局部变量
init(no1: Int) {
self.no1 = no1 // 初始化
}
}
class subClass: mainClass, tcpprotocol {
var no2: Int
init(no1: Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
required override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1: 20)
let show = subClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("res is: \(show.no1)")
print("res is: \(show.no2)")
res is: 20
res is: 30
res is: 50
协议类型
协议不实现任何功能,但是,协议可以作为类型使用
使用场景
1.作为函数 方法或者构造器中的参数类型或者返回值类型
2.作为常量 变量或者属性的类型
3.作为数组 字典或者其他容器中的元素类型
protocol Generator {
associatedtype members
func next() -> members?
}
var items = [10,20,30].makeIterator()
while let x = items.next() {
print(x)
}
for lists in [1,2,3].map( {i in i*5}) {
print(lists)
}
print([100,200,300])
print([1,2,3].map({i in i*10}))
10
20
30
5
10
15
[100, 200, 300]
[10, 20, 30]
在扩展中添加协议成员
protocol AgeClasificationProtocol {
var age: Int { get }
func agetype() -> String
}
class Person {
let firstname: String
let lastname: String
var age: Int
init(firstname: String, lastname: String) {
self.firstname = firstname
self.lastname = lastname
self.age = 10
}
}
extension Person : AgeClasificationProtocol {
func fullname() -> String {
var c: String
c = firstname + " " + lastname
return c
}
func agetype() -> String {
switch age {
case 0...2:
return "Baby"
case 2...12:
return "Child"
case 13...19:
return "Teenager"
case let x where x > 65:
return "Elderly"
default:
return "Normal"
}
}
}
协议的继承
协议能够继承一个或者多个其他协议,可以在继承的协议基础上添加新的内容要求
多个被继承的协议间用逗号分隔
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 协议定义
}
类专属协议
可以在协议的继承列表中 通过添加关键字class,限制协议只能适配到类(class)类型。
该class关键字必须是第一个出现在协议的继承列表中,其后,才是其他继承协议
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 协议定义
}
protocol TcpProtocol {
init(no1: Int)
}
class MainClass {
var no1: Int // 局部变量
init(no1: Int) {
self.no1 = no1 // 初始化
}
}
class SubClass: MainClass, TcpProtocol {
var no2: Int
init(no1: Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 因为遵循协议,需要加上"required"; 因为继承自父类,需要加上"override"
required override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("res is: \(show.no1)")
print("res is: \(show.no2)")
res is: 20
res is: 30
res is: 50
协议合成
protocol Stname {
var name: String { get }
}
protocol Stage {
var age: Int { get }
}
struct Person: Stname, Stage {
var name: String
var age: Int
}
func show(celebrator: Stname & Stage) {
print("\(celebrator.name) is \(celebrator.age) years old")
}
let studname = Person(name: "Priya", age: 21)
print(studname)
let stud = Person(name: "Rehan", age: 29)
print(stud)
let student = Person(name: "Roshan", age: 19)
print(student)
Person(name: "Priya", age: 21)
Person(name: "Rehan", age: 29)
Person(name: "Roshan", age: 19)
检查协议的一致性
可以使用is和as操作符来检查是否遵循某一协议或强制转化为某一类型
- is操作符用来检查实例是否遵循了某个协议。
- as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil。
- as用以强制向下转型,如果强转失败,会引起运行时错误。
protocol HasArea {
var area: Double { get }
}
// 定义了Circle类,都遵循了HasArea协议
class Circle: HasArea {
let pi = 3.1415927
var radius: Double
var area: Double { return pi * radius * radius }
init(radius: Double) { self.radius = radius }
}
// 定义了Country类,都遵循了HasArea协议
class Country: HasArea {
var area: Double
init(area: Double) { self.area = area }
}
// Animal是一个没有实现HasArea协议的类
class Animal {
var legs: Int
init(legs: Int) { self.legs = legs }
}
let objects: [AnyObject] = [
Circle(radius: 2.0),
Country(area: 243_610),
Animal(legs: 4)
]
for object in objects {
// 对迭代出的每一个元素进行检查,看它是否遵循了HasArea协议
if let objectWithArea = object as? HasArea {
print("面积为 \(objectWithArea.area)")
} else {
print("没有面积")
}
}
面积为 12.5663708
面积为 243610.0
没有面积
泛型
数组和字典类型都是泛型集
// 定义一个交换两个变量的函数
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var numb1 = 100
var numb2 = 200
print("交换前数据: \(numb1) 和 \(numb2)")
swapTwoInts(&numb1, &numb2)
print("交换后数据: \(numb1) 和 \(numb2)")
交换前数据: 100 和 200
交换后数据: 200 和 100
泛型使用了占位类型名(在这里用字母 T 来表示)来代替实际类型名
// 定义一个交换两个变量的函数
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)")
交换前数据: 100 和 200
交换后数据: 200 和 100
交换前数据: A 和 B
交换后数据: B 和 A
泛型类型
栈只允许在集合的末端添加新的元素(称之为入栈),且也只能从末端移除元素(称之为出栈)
网友评论