Swift学习笔记

一、简介

  • 苹果于2014年WWDC(苹果开发者大会)发布的新开发语言,可与Objective-C共同运行于Mac OS和iOS平台,用于搭建基于苹果平台的应用程序。
  • 2015年6月8日,苹果于WWDC 2015上宣布,Swift将开放源代码,包括编译器和标准库。

二、基本语法

1.分号

Swift不要求在每行语句的结尾使用分号(;),但当你在同一行书写多条语句时,必须用分号隔开。

var myString = "Hello, World!"; print(myString)

2.标识符

标识符就是给变量、常量、方法、函数、枚举、结构体、类、协议等指定的名字。

命名规则:

  • 区分大小写
  • 首字符可以以下划线(_)或者字母开始,但不能是数字。
  • 标识符中其他字符可以是下划线(_)、字母或数字。

注意点:
一般不允许使用关键字作为标识符,如果一定要使用关键字作为标识符,可以在关键字前后添加重音符号(`),例如:

let `class` = "Hello World"

3.空格的使用

Swift语言并不是像C/C++,Java那样完全忽视空格。
也不像Python对缩进的要求那么严格。
Swift对空格的使用有一定的要求。

错误1:  
let a= 1 + 2   
错误信息:error: prefix/postfix '=' is reserved
错误解释:等号直接跟在前面或后面这种用法是保留的。


错误2:
let a = 1+ 2
错误信息:error: consecutive statements on a line must be separated by ';'
错误解释:这是因为Swift认为到1+这个语句就结束了,2就是下一个语句了。

正确用法:
let a = 1 + 2  // 编码规范推荐使用这种写法
let b = 3+4    // 这样也不会报错

4.Swift字面量

所谓字面量,就是指像特定的数字,字符串或者是布尔值这样,能够直接了当地指出自己的类型并为变量进行赋值的值。

42                 // 整型字面量
3.14159            // 浮点型字面量
"Hello, world!"    // 字符串型字面量

let decimalInteger = 17           // 17 - 十进制表示
let binaryInteger = 0b10001       // 17 - 二进制表示
let octalInteger = 0o21           // 17 - 八进制表示
let hexadecimalInteger = 0x11     // 17 - 十六进制表示

// 布尔型字面量
true 表示真。
false 表示假。
nil 表示没有值。

三、数据类型

1.内置数据类型

Int

一般来说,你不需要专门指定整数的长度,Swift提供了一个特殊的整数类型Int,长度与当前平台的原生字长相同。

  • 在32位平台上,Int和Int32长度相同。
  • 在64位平台上,Int和Int64长度相同。

UInt

  • 在32位平台上,UInt和UInt32长度相同。
  • 在64位平台上,UInt和UInt64长度相同。

浮点数:Float、Double

Double:64位浮点数。
Float:32位浮点数。

Bool

true
false

字符串:String

字符串是字符的序列集合,例如:”Hello, World!”。

字符:Character

指单个字符,例如:”A”

可选类型:Optional

可选类型是用来处理值可能缺失的情况,或选类型表示“有值或没有值”。

2.数值范围

1.png

3.类型别名

说明:类型别名也就是给指定类型定义一个别名。

语法:typealias newName = type

示例:
// 给日本人起一个别名——日本鬼子
typealias Japs = Japanese;
// 创建一个日本鬼子实例
var aa: Japs = Japs();

4.类型安全

Swift是类型安全的语音,也就是说它会在编译期检查你的类型代码,并把不匹配的类型指出来。

示例:

var varA = 42
varA = "This is hello"
print(varA)

报错:
error: cannot assign value of type 'String' to type 'Int'
varA = "This is hello"

5.类型推断

如果你没有显式指定类型,Swift 会使用类型推断来选择合适的类型。

// 推断为Int
let a = 42

// 推断为double
let b = 3.38384

四、可选类型

Swift 的可选(Optional)类型,用于处理值缺失的情况。可选表示”那儿有一个值,并且它等于 x “或者”那儿没有值”。

声明方式:

var optionalInteger: Int?
var optionalInteger: Optional

注意:当声明一个数组的时候要写成(Int[])?,如果写成Int[]?会报错。

var myString:String? = nil

if myString != nil {
    print(myString)
}else{
    print("字符串为 nil")
}

程序执行结果为:
字符串为 nil

1.强制解析

当你确定可选类型确实包含值之后,你可以在可选的名字后面加一个感叹号(!)来获取值。这个感叹号表示”我知道这个可选有值,请使用它。”这被称为可选值的强制解析。

示例:

var myString:String?

myString = "Hello, Swift!"

if myString != nil {
   print(myString)
}else{
   print("myString 值为 nil")
}

结果为:Optional("Hello, Swift!")

使用强制解析

var myString:String?

myString = "Hello, Swift!"

if myString != nil {
   // 强制解析
   print( myString! )
}else{
   print("myString 值为 nil")
}

结果为:Hello, Swift!

==注意点:==

使用!来获取一个不存在的可选值会导致运行时错误。使用!来强制解析值之前,一定要确定可选包含一个非nil的值。

2.自动解析

你可以在声明可选变量时使用感叹号(!)替换问号(?)。这样可选变量在使用时就不需要再加一个感叹号(!)来获取值,它会自动解析。

var myString:String!

myString = "Hello, Swift!"

if myString != nil {
   print(myString)
}else{
   print("myString 值为 nil")
}

结果:Hello, Swift!

3.可选绑定

使用可选绑定(optional binding)来判断可选类型是否包含值,如果包含值就把值赋给一个临时常量或者变量。可选绑定可以用在if和while语句中来对可选类型的值进行判断并把值赋给一个常量或者变量。

if let constantName = someOptional {
    statements
}

示例:

var myString:String?

myString = "Hello, Swift!"

if let yourString = myString {
   print("你的字符串值为 - \(yourString)")
}else{
   print("你的字符串没有值")
}

结果:你的字符串值为 - Hello, Swift!

五、常量

1.语法:

let name = value

2.类型标注

语法:

let name:type = value

示例:

let a:Int = 5;

3.常量输出

以括号加反斜线插入

示例:

六、运算符

区间运算符

Swift提供了2种区间运算符:


2.png

七、字符串

1.创建字符串

// 使用字面量
var a = "hello world"

// 实例化
var b = String("hello world")

2.空字符串的创建与判断

// 使用字符串字面量创建空字符串
var stringA = ""

if stringA.isEmpty {
   print( "stringA 是空的" )
} else {
   print( "stringA 不是空的" )
}

// 实例化 String 类来创建空字符串
let stringB = String()

if stringB.isEmpty {
   print( "stringB 是空的" )
} else {
   print( "stringB 不是空的" )
}

3.字符串中插入值

import Cocoa

var varA   = 20
let constA = 100
var varC:Float = 20.0

var stringA = "\(varA) 乘于 \(constA) 等于 \(varC * 100)"
print( stringA )

4.字符串连接

let constA = "我叫"
let constB = "小明"

var stringA = constA + constB

print( stringA )

结果:我叫小明

区别于OC实现方式:

// 方法1
string = [NSString initWithFormat:@"%@,%@", string1, string2 ];

// 方法2
string = [string1 stringByAppendingString:string2];

// 方法3
string = [string stringByAppendingFormat:@"%@,%@",string1, string2];

5.字符串比较

var varA   = "Hello, Swift!"
var varB   = "Hello, World!"

if varA == varB {
   print( "\(varA) 与 \(varB) 是相等的" )
} else {
   print( "\(varA) 与 \(varB) 是不相等的" )
}

结果:Hello, Swift! 与 Hello, World! 是不相等的

八、数组

1.创建数组

创建数组:

var someArray = SomeType

创建初始化大小数组:

var someArray = [SomeType](repeating: InitialValue, count: NumbeOfElements)

示例:

// 创建了一个类型为 Int ,数量为 3,初始值为 0 的空数组
var someInts = [Int](repeating: 0, count: 3)

// 创建了含有三个元素的数组
var someInts:[Int] = [10, 20, 30]

2.访问数组

语法:var someVar = someArray[index]

示例:

var someInts = [Int](repeating: 10, count: 3)

var someVar = someInts[0]

print( "第一个元素的值 \(someVar)" )
print( "第二个元素的值 \(someInts[1])" )
print( "第三个元素的值 \(someInts[2])" )

输出:
第一个元素的值 10
第二个元素的值 10
第三个元素的值 10

3.修改数组

添加元素:

var someInts = [Int]()

someInts.append(20)
someInts.append(30)
someInts += [40]

var someVar = someInts[0]

print( "第一个元素的值 \(someVar)" )
print( "第二个元素的值 \(someInts[1])" )
print( "第三个元素的值 \(someInts[2])" )

输出:
第一个元素的值 20
第二个元素的值 30
第三个元素的值 40

修改元素:

someInts[2] = 50

4.遍历

for item in someStrs {
   print(item)
}

5.合并数组

var intsA = [Int](repeating: 2, count:2)
var intsB = [Int](repeating: 1, count:3)

var intsC = intsA + intsB

for item in intsC {
    print(item)
}

输出结果:
2
2
1
1
1

九、字典

1.创建字典

语法:var someDict = KeyType: ValueType

示例:

// 空字典
var someDict = [Int: String]()

// 带初始值
var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

2.访问字典

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

var someVar = someDict[1]

3.修改字典

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

// 方法1
someDict.updateValue("One 新的值", forKey: 1)

// 方法2
someDict[1] = "One 新的值"

4.移除

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

// 方法1
someDict.removeValue(forKey: 2)

// 方法2
someDict[2] = nil

5.遍历字典

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

for (key, value) in someDict {
   print("字典 key \(key) -  字典 value \(value)")
}

6.字典转数组

var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]

let dictKeys = [Int](someDict.keys)
let dictValues = [String](someDict.values)

十一、函数

1.定义

// 无参数函数
func funcname() -> datatype {
   return datatype
}

// 有参数函数
func runoob(site: String) -> String {
    return (site)
}

// 无返回值
func runoob(site: String) {
    print("\(site)")
}

元组作为函数返回值

func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1.. currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("最小值为 \(bounds.min) ,最大值为 \(bounds.max)")

输出结果:
最小值为 -6 ,最大值为 109

2.外部参数名和局部参数名

// firstArg     外部参数名
// a            局部参数名
func pow(firstArg a: Int, secondArg b: Int) -> Int {
   var res = a
   for _ in 1..

3.可变参数

func vari(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", "Alibaba")

输出:
4
3
5
4.5
3.1
5.6
Google
Baidu
Alibaba

4.常量、变量及I/O参数

一般默认在函数中定义的参数都是常量参数,也就是这个参数你只可以查询使用,不能改变它的值。如果想要声明一个变量参数,可以在参数定义前加 inout 关键字,这样就可以改变这个参数的值了。

func getName(_ name: inout String)

一般默认的参数传递都是传值调用的,而不是传引用。所以传入的参数在函数内改变,并不影响原来的那个参数。传入的只是这个参数的副本。当传入的参数作为输入输出参数时,需要在参数名前加&符,表示这个值可以被函数修改。

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)")

5.使用函数类型

定义一个叫做 addition 的变量,参数与返回值类型均是 Int ,并让这个新变量指向 sum 函数

func sum(a: Int, b: Int) -> Int {
   return a + b
}
var addition: (Int, Int) -> Int = sum
print("输出结果: \(addition(40, 89))")

输出:
129

函数类型作为参数类型、函数类型作为返回类型

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

6.函数嵌套

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

十二、闭包

1.说明

Swift中的闭包与C和OC中的代码块(blocks)以及其它语言中的匿名函数比较相似。
全局函数和嵌套函数其实就是特殊的闭包。

2.闭包的3种形式

  • 全局函数:有名字但不能捕获任何值。
  • 嵌套函数:有名字,也能捕获封闭函数内的值。
  • 装饰表达式:无名闭包,使用轻量级语法,可以根据上下文环境捕获值。

3.语法

{(parameters) -> return type in
   statements
}

示例:

let studname = { print("Swift 闭包实例。") }
studname()

输出:
Swift 闭包实例。

接收2个参数,并返回布尔值

let divide = {(val1: Int, val2: Int) -> Int in 
   return val1 / val2 
}
let result = divide(200, 20)
print (result)

输出:
10

4.闭包表达式

sorted方法

Swift 标准库提供了名为 sorted(by:)的方法,会根据你提供的用于排序的闭包函数将已知类型数组中的值进行排序。
排序完成后,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。

sorted(by:)方法需要传入两个参数:

  • 已知类型的数组
  • 闭包函数:该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前面,排序闭包函数需要返回 true,反之返回 false。

示例:

let names = ["A", "C", "B", "E", "D"]

// 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
func backwards(s1: String, s2: String) -> Bool {
    return s1 < s2
}
var reversed = names.sorted(by: backwards)

print(reversed)

输出:
["A", "B", "C", "D", "E"]

5.参数名称缩写

Swift 自动为内联函数提供了参数名称缩写功能,可以直接通过0,1,$2来顺序调用闭包的参数。

let names = ["A", "C", "B", "E", "D"]

var reversed = names.sorted( by: { $0 < $1 } )
print(reversed)

输出:
["A", "B", "C", "D", "E"]

6.参数名缩写

Swift 的String类型定义了关于大于号(>)和小于号(<)的字符串实现,其作为一个函数接受两个String类型的参数并返回Bool类型的值。 而这正好与sort(_:)方法的第二个参数需要的函数类型相符合。因此,你可以简单地传递一个大于号或小于号,Swift可以自动推断出您想使用大于号的字符串函数实现

let names = ["A", "C", "B", "E", "D"]

var reversed = names.sorted(by: <)
print(reversed)

输出:
["A", "B", "C", "D", "E"]

7.尾随闭包

尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

let names = ["A", "C", "B", "E", "D"]

//尾随闭包
var reversed = names.sorted() { $0 < $1 }
print(reversed)

sort() 后的 { $0 > $1} 为尾随闭包。
以上程序执行输出结果为:
["A", "B", "C", "D", "E"]

注意: 如果函数只需要闭包表达式一个参数,当使用尾随闭包时,甚至可以把()省略掉。

reversed = names.sorted { 1 }

8.捕获值

闭包可以在其定义的上下文中捕获常量或变量。
即使定义这些常量和变量的原域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
Swift最简单的闭包形式是嵌套函数,也就是定义在其他函数的函数体内的函数。
嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

incrementor函数并没有获取任何参数,但是在函数体内访问了runningTotal和amount变量。
这是因为其通过捕获在包含它的函数体内已经存在的runningTotal和amount变量而实现。
由于没有修改amount变量,incrementor实际上捕获并存储了该变量的一个副本,而该副本随着incrementor一同被存储。
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())

输出:
10
20
30

9.闭包是引用类型

上面的例子中,incrementByTen是常量,但是这些常量指向的闭包仍然可以增加其捕获的变量值。这是因为函数和闭包都是引用类型。

十三、结构体

1.说明

Swift 结构体是构建代码所用的一种通用且灵活的构造体。
我们可以为结构体定义属性(常量、变量)和添加方法,从而扩展结构体的功能。

2.语法

struct nameStruct { 
   Definition 1
   Definition 2
   ……
   Definition N
}

使用示例:

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)")

输出:
Mark1 是 100
Mark2 是 78
Mark3 是 98

结构体实例总是通过值传递来定义你的自定义数据类型

struct MarksStruct {
   var mark: Int

   init(mark: Int) {
      self.mark = mark
   }
}
var aStruct = MarksStruct(mark: 98)
var bStruct = aStruct // aStruct 和 bStruct 是使用相同值的结构体!
bStruct.mark = 97
print(aStruct.mark) // 98
print(bStruct.mark) // 97

输出:
98
97

3.应用场景

符合以下条件时可以考虑结构体:

  • 结构体的主要目的是用来封装少量相关简单数据值。
  • 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
  • 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
  • 结构体不需要去继承另一个已存在类型的属性或者行为。

具体场景举例

  • 几何形状的大小,封装一个width属性和height属性,两者均为Double类型。
  • 一定范围内的路径,封装一个start属性和length属性,两者均为Int类型。
  • 三维坐标系内一点,封装x,y和z属性,三者均为Double类型。

十四、类

1.语法

class student{
   var studname: String
   var mark: Int 
   var mark2: Int 
}

实例化:
let studrecord = student()

2.恒等运算符

因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例。

===
!==

十五、属性

1.说明

Swift属性跟特定的类、结构或枚举关联。

2.分类

  • 存储属性:存储常量或变量作为实例的一部分,用于类和结构体。
  • 计算属性:计算(而不是存储)一个值,用于类、结构体和枚举。

3.存储属性

存储属性就是存储在特定类或结构体的实例里的一个常量或变量。
存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)。

示例:

struct Number
{
   var digits: Int
   let pi = 3.1415
}

var n = Number(digits: 12345)
n.digits = 67

print("\(n.digits)")
print("\(n.pi)")

输出:
67
3.1415
  • 可以在定义存储属性的时候指定默认值。
  • 也可以在构造过程中设置或修改存储属性的值。

4.延迟存储属性

延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。
在属性声明前使用 lazy 来标示一个延迟存储属性。

应用场景

  • 延迟对象的创建
  • 当属性值依赖于其它未知类
class sample {
    lazy var no = number() // `var` 关键字是必须的
}

class number {
    var name = "Hello World"
}

var firstsample = sample()
print(firstsample.no.name)

输出:
Hellow World

5.计算属性

类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个getter来获取值,一个可选的 setter 来间接设置其他属性或变量的值。

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。

6.只读计算属性

只有 getter 没有 setter 的计算属性就是只读计算属性。
只读计算属性总是返回一个值,可以通过点(.)运算符访问,但不能设置新的值。

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关键字只用来声明常量属性,表示初始化后再也无法修改的值。

7.属性观察器

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。

  • willSet在设置新的值之前调用
  • didSet在新的值被设置之后立即调用
  • willSet和didSet观察器在属性初始化过程中不会被调用
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

8.类型属性

类型属性是作为类型定义的一部分写在类型最外层的花括号({})内。
使用关键字 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 值
   }
}
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.说明

可以认为是访问对象、集合或序列的快捷方式,不需要再调用实例的特定的赋值和访问方法。
举例来说,用下标脚本访问一个数组(Array)实例中的元素可以这样写someArray[index],访问字典(Dictionary)实例中的元素可以这样写 someDictionary[key]。

2.语法

subscript(index: Int) -> Int {
    get {
        // 用于下标脚本值的声明
    }
    set(newValue) {
        // 执行赋值操作
    }
}

示例1:

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])")

输出:
100 除以 9 等于 11
100 除以 2 等于 50
100 除以 3 等于 33
100 除以 5 等于 20
100 除以 7 等于 14

示例2:

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])

输出:
Sunday
Monday
Tuesday
Wednesday

十七、构造过程

1.说明

Swift 构造函数使用 init() 方法。
与 Objective-C 中的构造器不同,Swift的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

2.语法

// 无构造参数
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
    }
}

示例:

struct rectangle {
    var length: Double
    var breadth: Double
    init() {
        length = 6
        breadth = 12
    }
}

var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")

3.外部参数名

如果示想有外部参数名,可以用_ 表示变量

struct Rectangle {
    var length: Double?

    init(frombreadth breadth: Double) {
        length = breadth * 10
    }

    init(frombre bre: Double) {
        length = bre * 30
    }
    //不提供外部名字
    init(_ area: Double) {
        length = area
    }
}

4.构造过程中修改常量属性

只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。

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)")

输出:
面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

5.构造器的继承和重载

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)")

输出:
矩形: 4 边
五角型: 5 边

6.类的可失败构造器

如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器。

变量初始化失败可能的原因有:

  • 传入无效的参数值。
  • 缺少某种所需的外部资源。
  • 没有满足特定条件。

示例:

定义了一个名为Animal的结构体,其中有一个名为species的,String类型的常量属性。
同时该结构体还定义了一个,带一个String类型参数species的,可失败构造器。这个可失败构造器,被用来检查传入的参数是否为一个空字符串,如果为空字符串,则该可失败构造器,构建对象失败,否则成功。

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)")
}

十八、析构函数

1.说明

在一个类的实例被释放之前,析构函数被立即调用。用关键字deinit来标示析构函数,类似于初始化函数用init来标示。析构函数只适用于类类型。

2.原理

Swift 会自动释放不再需要的实例以释放资源。
Swift 通过自动引用计数(ARC)处理实例的内存管理。
通常当你的实例被释放时不需要手动地去清理。但是,当使用自己的资源时,你可能需要进行一些额外的清理。
例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前关闭该文件。

3.语法

deinit {
// 执行析构过程
}

示例:

var counter = 0;  // 引用计数器
class BaseClass {
    init() {
        counter += 1;
    }
    deinit {
        counter -= 1;
    }
}

var show: BaseClass? = BaseClass()
print(counter)
show = nil
print(counter)

输出:
1
0

十九、可选链

1.说明

可选链(Optional Chaining)是一种可以请求和调用属性、方法和子脚本的过程,用于请求或调用的目标可能为nil。
如果目标有值,调用就会成功,返回该值
如果目标为nil,调用将返回nil

2.示例

使用感叹号(!)可选链实例

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

想使用感叹号(!)强制解析获得这个人residence属性numberOfRooms属性值,将会引发运行时错误,因为这时没有可以供解析的residence值。

使用问号(?)可选链实例

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("不能查看房间号")
}

输出:
不能查看房间号

3.为可选链定义模型类

可以使用可选链来多层调用属性,方法,和下标脚本。这让你可以利用它们之间的复杂模型来获取更底层的属性,并检查是否可以成功获取此类底层属性。

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
        }
    }
}

4.通过可选链调用方法

你可以使用可选链的来调用可选值的方法并检查方法调用是否成功。即使这个方法没有返回值,你依然可以使用可选链来达成这一目的。

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("无法输出房间号")
}

输出:
无法输出房间号

5.使用可选链调用下标脚本

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("无法检索到房间")
}

输出:
无法检索到房间

二十、自动引用计数(ARC)

1.说明

Swift 使用自动引用计数(ARC)这一机制来跟踪和管理应用程序的内存。通常情况下我们不需要去手动释放内存,因为 ARC 会在类的实例不再被使用时,自动释放其占用的内存。但在有些时候我们还是需要在代码中实现内存管理。

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: "小明")

//赋值给其他两个变量,该实例又会多出两个强引用
reference2 = reference1
reference3 = reference1

//断开第一个强引用
reference1 = nil
//断开第二个强引用
reference2 = nil
//断开第三个强引用,并调用析构函数
reference3 = nil

输出:
小明开始初始化
小明被析构

2.类实例之间的循环强引用

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

解决实例之间的循环强引用

Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:

  • 弱引用:对于生命周期中会变为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

3.闭包引起的循环强引用

循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了实例。这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如self.someMethod。这两种情况都导致了闭包 “捕获” self,从而产生了循环强引用

class HTMLElement {

    let name: String
    let text: String?

    lazy var asHTML: () -> String = {
        if let text = self.text {
            return "<\(self.name)>\(text)"
        } 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)"
        } 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

输出:

hello, world

p 被析构

二十一、类型转换

1.说明

Swift 语言类型转换可以判断实例的类型。也可以用于检测实例类型是否属于其父类或者子类的实例。

Swift 中类型转换使用 is 和 as 操作符实现,is 用于检测值的类型,as 用于转换类型。

类型转换也可以用来检查一个类是否实现了某个协议。

2.向下转型

向下转型,用类型转换操作符(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)")
    }
}

输出:
实例物理学是: 固体物理
实例方程式: 赫兹
实例物理学是: 流体动力学
实例公式是: 千兆赫
化学主题是: '固体物理', 赫兹
数学主题是: '流体动力学',  千兆赫
化学主题是: '热物理学', 分贝
数学主题是: '天体物理学',  兆赫
数学主题是: '微分方程',  余弦级数

3.Any和AnyObject的类型转换

Swift为不确定类型提供了两种特殊类型别名:

  • AnyObject可以代表任何class类型的实例。
  • Any可以表示任何类型,包括方法类型(function types)。

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 实例
主题 '固体物理', 兆赫

二十二、Swift扩展

1.说明

扩展就是向一个已有的类、结构体或枚举类型添加新功能。

2.语法

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

3.构造器

扩展可以向已有类型添加新的构造器。

这可以让你扩展其它类型,将你自己的定制类型作为构造器参数,或者提供该类型的原始实现中没有包含的额外初始化选项。

扩展可以向类中添加新的便利构造器 init(),但是它们不能向类中添加新的指定构造器或析构函数 deinit() 。

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)

4.方法

extension Int {
   func topics(summation: () -> ()) {
      for _ in 0..

5.可变实例方法

通过扩展添加的实例方法也可以修改该实例本身。

结构体和枚举类型中修改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

二十三、协议

1.语法

// 定义协议
protocol SomeProtocol {
    // 协议内容
}

// 实现多个协议
struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 结构体内容
}

// 如果有父类,应该把父类放到最前面
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 类的内容
}

2.对属性的规定

协议用于指定特定的实例属性或类属性,而不用指定是存储型属性或计算型属性。此外还必须指明是只读的还是可读可写的。

协议中的通常用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

3.对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

4.对构造器的规定

协议可以要求它的遵循者实现指定的构造器。

你可以像书写普通的构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体,语法如下:

protocol SomeProtocol {
   init(someParameter: Int)
}

5.协议构造器在类中的实现

你可以在遵循该协议的类中实现构造器,并指定其为类的指定构造器或者便利构造器。在这两种情况下,你都必须给构造器实现标上”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

6.协议合成

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)

7.检验协议的一致性

你可以使用is和as操作符来检查是否遵循某一协议或强制转化为某一类型。

  • is操作符用来检查实例是否遵循了某个协议。
  • as?返回一个可选值,当实例遵循协议时,返回该协议类型;否则返回nil。
  • as用以强制向下转型,如果强转失败,会引起运行时错误。

二十四、访问控制

1.说明

  • public 可以访问自己模块中源文件里的任何实体,别人也可以通过引入该模块来访问源文件里的所有实体。
  • internal 可以访问自己模块中源文件里的任何实体,但是别人不能访问该模块中源文件里的实体。
  • fileprivate 文件内私有,只能在当前源文件中使用。
  • private 只能在类中访问,离开了这个类或者结构体的作用域外面就无法访问。

2.实例

public class SomePublicClass {}
internal class SomeInternalClass {}
fileprivate class SomeFilePrivateClass {}
private class SomePrivateClass {}

public var somePublicVariable = 0
internal let someInternalConstant = 0
fileprivate func someFilePrivateFunction() {}
private func somePrivateFunction() {}

除非有特殊的说明,否则实体都使用默认的访问级别 internal。
class SomeInternalClass {}              // 访问级别为 internal
let someInternalConstant = 0            // 访问级别为 internal

3.函数类型访问权限

函数的访问级别需要根据该函数的参数类型和返回类型的访问级别得出。

下面的例子定义了一个名为someFunction全局函数,并且没有明确地申明其访问级别。


func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

函数中其中一个类 SomeInternalClass 的访问级别是 internal,另一个 SomePrivateClass 的访问级别是 private。所以根据元组访问级别的原则,该元组的访问级别是 private(元组的访问级别与元组中访问级别最低的类型一致)。

因为该函数返回类型的访问级别是 private,所以你必须使用 private 修饰符,明确的声明该函数:

private func someFunction() -> (SomeInternalClass, SomePrivateClass) {
    // 函数实现
}

将该函数申明为 public 或 internal,或者使用默认的访问级别 internal 都是错误的,因为如果这样你就无法访问 private 级别的返回值。

4.枚举类型访问权限

枚举中成员的访问级别继承自该枚举,你不能为枚举中的成员单独申明不同的访问级别。

比如下面的例子,枚举 Student 被明确的申明为 public 级别,那么它的成员 Name,Mark 的访问级别同样也是 public:

public enum Student {
    case Name(String)
    case Mark(Int,Int,Int)
}

var studDetails = Student.Name("Swift")
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)")
}

5.子类访问权限

子类的访问级别不得高于父类的访问级别。比如说,父类的访问级别是internal,子类的访问级别就不能申明为public。

public class SuperClass {
    fileprivate func show() {
        print("超类")
    }
}

// 访问级别不能低于超类 internal > public
internal class SubClass: SuperClass  {
    override internal func show() {
        print("子类")
    }
}

6.协议访问权限

如果想为一个协议明确的申明访问级别,那么需要注意一点,就是你要确保该协议只在你申明的访问级别作用域中使用。

如果你定义了一个public访问级别的协议,那么实现该协议提供的必要函数也会是public的访问级别。这一点不同于其他类型,比如,public访问级别的其他类型,他们成员的访问级别为internal。

7.扩展访问权限

你可以在条件允许的情况下对类、结构体、枚举进行扩展。扩展成员应该具有和原始类成员一致的访问级别。比如你扩展了一个公共类型,那么你新加的成员应该具有和原始成员一样的默认的internal访问级别。

或者,你可以明确申明扩展的访问级别(比如使用private extension)给该扩展内所有成员申明一个新的默认访问级别。这个新的默认访问级别仍然可以被单独成员所申明的访问级别所覆盖

https://blog.csdn.net/haha223545/article/details/81624409

你可能感兴趣的:(Swift学习笔记)