swift社区:http://www.swift51.com (开源代码+项目+框架)
swift与OC区别:swift与OC
1、Swift容易阅读,语法和文件结构简易化。2、Swift更易于维护,文件分离后结构更清晰。3、Swift更加安全,它是类型安全的语言。4、Swift代码更少,简洁的语法,可以省去大量冗余代码5、Swift速度更快,运算性能更高。
2、纯Swift类的函数调用已经不是OC那样的运行时消息。而是类似C++的vtable,在编译的时候,就决定调用哪个函数了。不像OC在运行时才确定调用哪个函数。对于纯的Swift类来说,无法通过objc runtime替换方法,拿不到这些方法和属性。
swift中继承OC的类,仍然可以用runtime
3、Swift 是一门 类型安全 的语言:类型不一致的值是赋值不了的!Swift 是类型安全的,所以它会在编译你的代码时进行类型检查(type checks),并把不匹配的类型标记为错误。这可以让你在开发的时候尽早发现并修复错误。
【Swift 是一个类型安全(type safe)的语言。类型安全的语言可以让你清楚地知道代码要处理的值的类型。】
Swift 标准库是基于泛型代码构建的。
在定义变量时,你可以 显式指定类型 也可以不显示,swift会自动 类型推断,赋值的第一个值会进行类型判断。
1>可选类型,提供值是否有值得判断
2>特有的类属性,static和class修饰类属性和类方法。属性可以被重写,final禁止重写,
继承来的常量属性 不能修改,override:重写,convenience:便利构造器
其他关键字: lazy:懒加载 mutating:值类型内部函数会修改本身使用的关键字修饰函数,函数结束后,本身会赋值新值! subscript:下标 extension:扩展 protocol:协议 associatedtype:协议中泛型
3>结构体、枚举可以添加实例方法
4、最大的特点!构造必须初始化属性,子类指定构造器内要调用父类的指定构造器!!!!!!!!!
5、函数、协议 都可作为类型 ——> 都是 引用类型!
6、swift没有分类,只有扩展和协议,协议也可以进行扩展!(协议中扩展 直接在扩展中实现方法!)【都可以添加 类方法、对象方法】
OC中可以 添加 属性—>本质添加了set/get方法,需要动态添加属性。可添加已有的方法,进行覆盖!
Swift可以添加属性,但是只是 「计算属性」—>本质也是 set/get方法!不可以添加已有的方法!
不能添加「指定构造器」!可添加「便利构造器」
7、swift中宏:通过常量 和 函数 来代替。 swift中宏代替办法
【9、swift中 函数、闭包、类 都是引用类型。其他都是 值类型!!】
Swift 中所有的基本类型:整数(integer)、浮点数(floating-point number)、布尔值(boolean)、字符串(string)、数组(array)和字典(dictionary),都是值类型,其底层也是使用结构体实现的。
Swift 中所有的结构体和枚举类型都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型的属性,在代码中传递的时候都会被复制。
类是引用类型!!!!!
GCD的swift用法:https://www.jianshu.com/p/9e9fd6e5209e
注意
标准库定义的集合,例如数组,字典和字符串,都对复制进行了优化以降低性能成本。新集合不会立即复制,而是跟原集合共享同一份内存,共享同样的元素。在集合的某个副本要被修改前,才会复制它的元素。而你在代码中看起来就像是立即发生了复制。
值类型在传递过程中 先共享内存,指针拷贝,当有一方要变化时,则进行复制新的给它。
一、基础部分
【一般来说你很少需要写类型注解。如果你在声明常量或者变量的时候赋了一个初始值,Swift 可以推断出这个常量或者变量的类型,请参考 类型安全和类型推断。在上面的例子中,没有给 welcomeMessage 赋初始值,所以变量 welcomeMessage 的类型是通过一个 类型注解 指定的,而不是通过 初始值推断 的。】
1、可以不写分号作为结尾、var 定义变量、let定义常量
2、当推断浮点数的类型时,Swift 总是会选择 Double 而不是 Float。
Int 一般来说,你不需要专门指定整数的长度。【Swift 提供了一个特殊的整数类型 Int,长度与当前平台的原生字长相同.】
3、一个十进制数,没有前缀一个二进制数,前缀是 0b一个八进制数,前缀是 0o一个十六进制数,前缀是 0x
4、Swift 有一个基本的布尔(Boolean)类型,叫做 Bool。布尔值指逻辑上的值,因为它们只能是真或者假。Swift 有两个布尔常量,true 和 false
5、元祖——>let apples = ("小王",2,"岁了") let (name,age,mes) = apples; print(apples,"---",name,age,mes)
6、可选类型(optionals)来处理值可能缺失的情况。可选类型表示两种可能: 或者有值, 或者根本没有值。可选类型 类型后面加问号 比如: Int ?
你可以给可选变量赋值为 nil 来表示它没有值, nil 不能用于非可选的常量和变量 。如果你的代码中有常量或者变量需要处理值缺失的情况,请把它们声明成对应的可选类型。
如果你声明一个可选常量或者变量但是没有赋值,它们会自动被设置为 nil
7、nil 是一个指向不存在对象的指针。在 Swift 中,nil 不是指针——它是一个确定的值,用来表示 值缺失 。任何类型的可选状态都可以被设置为 nil,不只是对象类型。nil可以赋值 任何类型!(数据类型、结构体、类),nil是可以存储到 数组和字典中。
8、强制解析:
if convertedNumber != nil {print("convertedNumber has an integer value of \( convertedNumber! ).")} 强制解析
9、可选绑定 解析
if let actualNumber = Int(possibleNumber) {print("\'\(possibleNumber)\' has an integer value of \(actualNumber)")} else {print("\'\(possibleNumber)\' could not be converted to an integer")}// 输出“'123' has an integer value of 123”
10、?与!的区别,var a != 5 代表变量a一定有值!
二、基本运算符
1、运算溢出问题
算术运算符(+,-,*,/,% 等)的 结果会被检测并禁止值溢出,以此来避免保存变量时由于变量大于或小于其类型所能承载的范围时导致的异常结果。当然允许你使用 Swift 的溢出运算符来实现溢出
2、字符串也可以进行 + 号进行拼接
加法运算符也可用于 String 的拼接:"hello, " + "world"
3、等于(a == b)不等于(a != b)大于(a > b)小于(a < b)大于等于(a >= b)小于等于(a <= b) Swift 也提供恒等(===)和不恒等(!==)
Swift 也提供恒等(===)和不恒等(!==)这两个比较符来 判断 两个对象 是否引用 同一个对象实例。
4、元祖的比较:比较元组大小会按照从左到右、逐值比较的方式,直到发现有两个值不等时停止。如果所有的值都相等,那么这一对元组我们就称它们是相等的。
5、空合运算符(a ?? b)将对可选类型 a 进行空判断,如果 a 包含一个值就进行解包,否则就返回一个默认值 b。表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致。
例:a ?? b <——> a != nil ? a! : b //这两个是等价的 : a有值 取a,否则取 b
6、区间运算符 :1、a...b —> a<= x <=b 2、a..<b —> a<= x <b 3、a<..b —> a< x <=b
单侧区间 4、2... ——> 从索引 2 到结尾的所有值的区间 例如:for name in names [2...]
7、逻辑运算符 1、逻辑非(!a)2、逻辑与(a && b)3、逻辑或(a || b)
Swift 逻辑操作符 && 和 || 是左结合的,这意味着拥有多元逻辑操作符的复合表达式优先计算最左边的子表达式。(使用括号来明确优先级)
8、swift的多行注释是可以嵌套的,Swift 并不强制要求你在每条语句的结尾处使用分号(;),当然,你也可以按照你自己的习惯添加分号。有一种情况下必须要用分号,即你打算在同一行内写多条独立的语句必须使用分号。
多行字符三、字符串和字符
1>多行字符串:由一对 【三个双引号】 包裹着的具有固定顺序的文本字符集:
2、转义字符:转义字符 \0(空字符)、\\(反斜线)、\t(水平制表符)、\n(换行符)、\r(回车符)、\"(双引号)、\'(单引号)。Unicode 标量,写成 \u{n}(u 为小写),其中 n 为任意一到八位十六进制数且可用的 Unicode 位码。
3、字符串是 值类型,字符串在传递是 值拷贝
4、字符串可进行 + 号计算拼接
5、print("这个字符是\(char)") \(变量) 这样就可以把变量放进去
6、字符串遍历:你可通过 for-in 循环来遍历字符串,获取字符串中每一个字符的值
for character in "Dogaaaaa" { }
7、字符串插值:let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
四、集合类型: 数组是 有序 数据的集。集合是 无序无重复 数据的集。字典是无序的键值对的集。
1>数组: 类型—>Array<Element> —>简化 [Element]
初始化:var someInts = Array<Int>()
简化 var someInts = [Int]()
var shoppingList: [String] = ["Eggs", "Milk"]
2>集合: 类型—>Set<Element>
初始化:var favoriteGenres = Set<String>()
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
3>字典: 类型—>Dictionary<Key, Value> —> [Key: Value]
初始化: var namesOfIntegers = [Int: String]()
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
1、Swift 中的数组、集合和字典 必须明确 其中保存的键和值类型,这样就可以避免插入一个错误数据类型的值。同理,对于获取到的值你也可以放心,其数据类型是确定的
2、数组可以进行 用”+“号进行相加
3、添加:append(_:) 方法在数组后面添加新的数据项:
修改:shoppingList[0] = "Six eggs"
拆入:insert(_:at:) 方法在某个指定索引值之前添加数据项:
删除:remove(at:) 方法来移除数组中的某一项
遍历:数组—> for item in array {} 字典—>for (key,value) in dic {} //每一个字典中的数据项都以 (key, value) 元组 形式返回
4、集合(Sets)集合用来存储相同类型并且没有确定顺序的值。当集合元素顺序不重要时或者希望确保 每个元素只出现一次 时可以使用集合而不是数组!!!
Array()/[Int]() Set()
5、集合的可变性:如果创建一个数组、集合或字典 分配给变量就是可变的!分配给常量就是不可变的!
五、控制流
1、for-in 语句
for index in 1...5 {print("\(index) times 5 is \(index * 5)")}//index是变量
for _ in 1...power {answer *= base} //_来忽略变量
2、while 与 repeat-while (C中的do...while)
3、if语句
4、switch ——> case的值 可以是任意类型
5、控制语句关键词:continue、break 和 fallthrough(用在switch中贯穿)
6、带标签语句 goName: for index in 1...5 { print(index) break goName } // name:控制语句
7、控制字:continue break fallthrough return:提前退出 throw
输入输出参数 函数类型作为返回类型六、函数
1、有参数有返回值 func greet (person: String) -> String {let greeting = "Hello, " + person + "!"return greeting}
print(greetAgain(person: "Anna"))
2、无参函数 func sayHelloWorld() -> String {return "hello, world"}
3、多参函数 func greet(person: String, alreadyGreeted: Bool) -> String {}
4、无返回值函数 func greet(person: String) {print("Hello, \(person)!")}
5、多重返回值函数(返回值为元祖) func minMax(array: [Int]) -> (min: Int, max: Int) {}
6、可选元组返回类型 func minMax(array: [Int]) -> (min: Int, max: Int)? {}
7、隐式返回的函数 func greeting(for person: String) -> String {"Hello, " + person + "!"}//return可以省略(当 整个函数体 是一个单行表达式)
8、指定参数标签 你可以在参数名称前指定它的参数标签,中间以空格分隔
func greet(person: String, from hometown: String) -> String {return "Hello \(person)! Glad you could visit from \(hometown)."}
9、【默认参数值】 当默认值被定义后,调用这个函数时可以忽略这个参数。!!!!!!!!!!!!!!!!!
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12 ) {// 如果你在调用时候不传第二个参数,parameterWithDefault 会值为 12 传入到函数体中。}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault = 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault = 12
10、可变参数
func arithmeticMean(_ numbers: Double...) -> Double {var total: Double = 0for number in numbers {total += number}return total / Double(numbers.count)}
arithmeticMean(1, 2, 3, 4, 5)// 返回 3.0, 是这 5 个数的平均数。arithmeticMean(3, 8.25, 18.75)// 返回 10.0, 是这 3 个数的平均数。
11、输入输出参数 func swap(_ a: inout Int, _ b: inout Int) ——> swap(&a, &b) ——> 函数内a 和 b 的值在函数内部会修改外部!!
12、函数类型 :(Int, Int) -> Int 、() -> Void 代表函数类型
函数类型可以作为一个类型 进行 常量/变量 的开辟
func addTwoInts(_ a: Int, _ b: Int) -> Int {return a + b}
var mathFunction: (Int, Int) -> Int = addTwoInts
print("Result: \ (mathFunction(2, 3) )")
函数类型作为参数类型 :
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {print("Result: \(mathFunction(a, b))")}
函数类型作为返回类型
func chooseStepFunction(backward: Bool) -> (Int) -> Int {return backward ? stepBackward : stepForward}
10、嵌套函数
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward }
var currentValue = -4let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
尾随闭包 闭包实例 闭包特点七、闭包 其实就是一个没有名字的函数!!!
所谓闭包就是封闭的、自包含的代码块。 属于函数的匿名简化版,以下是一个闭包的形式:{ (形参列表) ->返回值类型 in 执行体(相当于函数体) }
1>概念:利用上下文推断参数和返回值类型 、隐式返回单表达式闭包,即单表达式闭包可以省略 return 、关键字参数名称缩写 、尾随闭包语法
2>闭包表达式语法!!!!
{ (parameters) ->return type in statements}//闭包的函数体部分由关键字 in 引入
简单闭包:reversedNames = names.sorted( by: { (s1: String, s2: String) -> Bool in return s1 > s2} )
3、可以使用简化参数名,如$0, $1(从0开始,表示第i个参数...)
4、尾随闭包
// 以下是使用尾随闭包进行函数调用someFunction() {// 闭包主体部分}
5、闭包是引用类型!!!!! 函数和闭包都是引用类型。
6、函数、闭包 值捕获:
为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,Swift 可能会改为捕获并保存一份对 【值的拷贝】。Swift 也会负责 【被捕获变量的所有内存管理工作】,包括释放不再需要的变量。
7、逃逸闭包 @escaping:当一个 【闭包作为 参数】 传到一个函数中,但是这个 【闭包在函数 返回之后 才被执行】,我们称该闭包从函数中逃逸。定义接受闭包作为参数的函数时,你可以在参数名之前标注 【@escaping】
8、自动闭包 @autoclosure (能不用就不用):(前提,闭包里就 【一行表达式的闭包】 适合 用自动闭包!!)闭包里的执行代码 (直接—>去掉大括号) 作为参数 为显式的闭包。——> 这个代码不会执行,只是一个参数,在函数里闭包被调用时才会执行!有延迟执行的效果!
过度使用 autoclosures 会让你的代码变得难以理解。上下文和函数名应该能够清晰地表明求值是被延迟执行的。
八、枚举 (swift中枚举,可以理解成结构体,里面的枚举类型为 他的 属性!!枚举每个元素可以放入 数组、字典 中)
1、Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值(称为原始值),则该值的类型可以是字符串、字符,或是一个整型值或浮点数、元祖。
enum CompassPoint {
case north
case south
case east
case west}
【与 C 和 Objective-C 不同,Swift 的枚举成员在被创建时 ——> 不会被赋予一个默认的整型值!!。在上面的 CompassPoint 例子中,north,south,east 和 west 不会被隐式地赋值为 0,1,2 和 3。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的 CompassPoint 类型。】
2、enum Planet {case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune}//这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的 CompassPoint 类型。
【使用时,变量的类型不明确,则使用 Enum.item 如果变量明确 直接使用 .item 】!!!
3、枚举成员的遍历——>令枚举遵循 CaseIterable 协议——> Swift 会生成一个 【allCases 属性】
enum Beverage: CaseIterable {case coffee, tea, juice}
【for beverage in Beverage.allCases {print(beverage)}】
4、关联值 【代存储】——> 可以定义 Swift 枚举来存储任意类型的关联值!!在外部进行 赋值 存储!(前提设置 枚举item的类型!) 关联值 三法 ——> 设、赋、取!
【枚举还是枚举,遍历时还是一样的逻辑,只是 关联值 类似 可以顺便存储值!遍历时可以取的】
enum Barcode {case upc(Int, Int, Int, Int) //1>设置类型 (这里括号类型 与 枚举元素赋值类型不是一致的,赋值的类型为 枚举的类型!)
case qrCode(String)}
var productBarcode = Barcode.upc(8, 85909, 51226, 3) //2>在外部 赋值 进行存储 值
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
//实例
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check): // 3> 定义变量 取 关联值 —> 类似 变量解析!
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")}
5、原始值 rawValue属性 【枚举成员可以被默认值(称为原始值)预填充,这些原始值的 类型必须相同!。设置 枚举的类型!】
enum ASCIIControlCharacter: Character //1> 必须制定类型!!
{
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"}
原始值和关联值是不同的。原始值是在定义枚举时被预先填充的值,像上述三个 ASCII 码。对于一个特定的 枚举成员,它的原始值始终不变 。关联值是创建一个基于枚举成员的常量或变量时才设置的值,枚举成员的关联值可以变化。
6、原始值的隐式赋值:【在使用 原始值 为 整数或者字符串 类型的枚举时,不需要显式地为每一个枚举成员设置原始值,Swift 将会自动为你赋值。】
【只有设置 枚举类型,才有原始值!!才有属性 rawValue】
整数作为原始值时,隐式赋值的值依次递增 1。如果第一个枚举成员没有设置原始值,其原始值将为 0。
枚举的关联值是实际值,并不是原始值的另一种表达方法。实际上,如果没有比较有意义的原始值,你就不需要提供原始值。
enum Planet: Int { case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune} // 后面的item 依次 23456
7、使用 原始值初始化 枚举实例 :let possiblePlanet = Planet(rawValue: 7)// 获取 item为7 的 枚举实例,且类型 为 原始值为7的item类型
8、递归枚举:使用枚举内元素作为元素(暂且不用考虑)
九、类和结构体
1>结构体和类对比
1-定义属性用于存储值 2-定义方法用于提供功能 3-定义下标操作用于通过下标语法访问它们的值//成员变量 4-定义构造器用于设置初始值!!!//init方法 5-通过扩展以增加默认实现之外的功能 6-遵循协议以提供某种标准功能 7-继承功能
2>类型定义的语法
struct SomeStructure {
// 在这里定义结构体}
class SomeClass {
// 在这里定义类}
3、结构体和类的实例(初始化!)
let someResolution = Resolution()
let someVideoMode = VideoMode()
4、结构体类型 的成员逐一构造器(默认构造器!)——> 结构体默认是 有Init(...)初始化方法!
let vga = Resolution(width: 640, height: 480)
5、结构体和枚举 是值类型 ——>值类型是这样一种类型,当它被赋值给一个变量、常量或者被传递给一个函数的时候,其值会被拷贝。(但是 swift中集合、数组、字典 被优化了,先共用 一个内存空间,如有变动则会再拷贝一份!)
6、类是引用类型——>引用类型在被赋予到一个变量、常量或者被传递到一个函数时,其值不会被拷贝。因此,使用的是已存在实例的引用,而不是其拷贝。
7、 恒等运算符——>判定两个常量或者变量是否引用同一个类实例有时很有用
相同(===)不相同(!==)——>使用这两个运算符 检测两个常量或者变量是否引用了同一个实例: (注意===与==区别,一个是否相同,一个是否相等)
8、指针:Swift 中引用了某个引用类型实例的常量或变量,与 C 语言中的指针类似,不过它并不直接指向某个内存地址,也不要求你使用星号(*)来表明你在创建一个引用。相反,Swift 中引用的定义方式与其它的常量或变量的一样。swift的变量直接指向该内存地址!而oc中变量指向指针地址,*p指向对象地址!
十、属性
1、存储属性:一个存储属性就是存储在特定类或结构体实例里的一个常量或变量。
2、延时加载存储属性:在属性声明前使用 lazy 来标示一个延时加载存储属性。
class DataManager {
lazy var importer = DataImporter() //调用这个属性的时候,才会进行赋值
var data = [String]()// 这里会提供数据管理功能}
3、 计算属性:相当于增加了 set和get方法!!
【Swift 中的属性 —> 没有对应的实例变量,属性的备份存储也无法直接访问。除存储属性外,类、结构体和枚举可以定义计算属性。->计算属性不直接存储值<-,而是提供一个 getter 和一个可选的 setter,来 ->间接 【获取和设置】——> 【其他】属性或变量的值!!。】
var center: Point {
get {
let centerX = origin.x + (size.width / 2)let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)origin.y = newCenter.y - (size.height / 2)}}
【如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 newValue】
简化set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)}
简化get {
Point(x: origin.x + (size.width / 2),y: origin.y + (size.height / 2))}
4、必须使用 var 关键字定义计算属性,包括 只读计算属性,因为它们的值不是固定的。let 关键字只用来声明常量属性,表示初始化后再也无法修改的值。
var volume: Double { return width * height * depth }//只把get留下来
5、属性观察器
在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的 willSet 和 didSet 观察器。而在父类初始化方法调用之前,给子类的属性赋值时不会调用子类属性的观察器。
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("将 totalSteps 的值设置为 \(newTotalSteps)")}
didSet {
if totalSteps > oldValue {print("增加了 \(totalSteps - oldValue) 步")}//oldValue为固定旧值名称!!!这说明在willSet的方法中属性已经被修改!!!
}}}
6、类型属性:类属性,非实例属性(区别OC):无论创建了多少个该类型的实例,这些属性都只有唯一一份。这种属性就是类型属性。它的作用范围也就在类型支持的范围内。static和class关键字进行修饰!!
跟实例的存储型属性不同,必须给存储型类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化。即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用 lazy 修饰符。
总结:类属性,必须赋值!必须static和class关键字进行修饰!!只第一次访问时才初始化,且只初始化一次!
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {return 27}
class var overrideableComputedTypeProperty: Int {return 107}}//class修饰,子类可重写
6、获取和设置类型属性的值:通过类的点语法来获取类属性值!
SomeClass.storedTypeProperty
7、全局变量和局部变量:【全局变量】是在函数、方法、闭包或任何类型之外定义的变量。【局部变量】是在函数、方法或闭包 内部定义的变量。
注意:全局的常量或变量都是延迟计算的,跟 延时加载存储属性 相似,不同的地方在于,全局的常量或变量不需要标记 lazy 修饰符。局部范围的常量和变量从不延迟计算。
十一、方法
1、类、结构体、枚举都可以定义 实例方法 / 类型方法,结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一
2、self 属性:类型的每一个实例都有一个隐含属性叫做 self!!!,self 完全等同于该实例本身。在swift中你不必在你的代码里面经常写 self!!!类中的属性和函数,可以直接用不必用self,不写self的话,swift默认帮你使用self。
3、结构体和枚举是值类型。默认情况下,值类型的属性 不能在它的实例方法中被修改。
struct Point {
var x = 0.0, y = 0.0
1>mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaXy += deltaY}
2>mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)}//1和2的效果一样!
}//这个方法做的任何改变都会在 方法执行结束时 写回到原始结构中. 这个新实例在 方法结束时 会 替换现存实例。!!!!!——>有这个方法的结构体不可以用let修饰!!
4、类型方法:函数名前添加static修饰为类方法,但子类不可以重写,class修饰的类方法则可以子类进行重写。!
十二、下标:属性下标、类下标
1、定义下标使用 subscript 关键字,与定义实例方法类似,都是指定一个或多个输入参数和一个返回类型。与实例方法不同的是,下标可以设定为读写或只读。这种行为由 getter 和 setter 实现,类似计算型属性:
subscript(index: Int) -> Int {
get {
// 返回一个适当的 Int 类型的值}
set(newValue) {
// 执行适当的赋值操作}}//newValue 的类型和下标操作的返回类型相同。
//这里类似 计算属性
2、下标可以接受 任意数量的入参,并且这些 入参可以是任意类型。下标的返回值也可以是任意类型。下标可以使用可变参数,但是不能使用 in-out 参数以及不能提供默认参数。
3、类型下标:static和class修饰为类下标
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)! } }
let mars = Planet[4]
十三、继承——> swift中只有类具有 集成功能,结构体没有集成功能!
1、基类:Swift 中的类并不是从一个通用的基类继承而来 (与OC的区别,OC中都是继承NSObject)的。如果你不为自己定义的类指定一个超类的话,这个类就会自动成为基类。
2、重写:子类可以为继承来的 实例方法,类方法,实例属性,类属性,或下标提供自己定制的实现。我们把这种行为叫重写。(在重写之前加 override 关键字)
意外的重写行为可能会导致不可预知的错误,任何缺少 override 关键字的重写都会在编译时被认定为错误
访问超类的方法,属性及下标:super 前缀来访问超类版本的方法,属性或下标
3、重写属性:实质:重写属性的 Getters 和 Setters —>必须重写get方法!否则使用 super.key
如果你在重写属性中提供了 setter,那么你也一定要提供 getter。如果你不想在重写版本中的 getter 里修改继承来的属性值,你可以直接通过 super.someProperty 来返回继承来的值,其中 someProperty 是你要重写的属性的名字。
class Car: Vehicle {
var gear = 1
override func makeNoise() {
print("Choo Choo")}
override var description: String {
return super.description + " in gear \(gear)"}}
4、防止重写:方法,属性或下标标记为 final 来防止它们被重写
指定构造器(不同的形参) 便利构造器(加关键字,指定构造器)项目设置中改为swift4.0十四、构造方法init方法(swift中属性不能处于未知状态!不管是父类还是子类,属性都必须初始化!)当你为存储型属性分配默认值或者 在构造器中为设置初始值 时,它们的值是被直接设置的,不会触发任何属性观察者。 (也就是说swift中的属性必须有值!!否则使用可选类型)
1、默认构造器(或者属性默认设值!)
init() {
// 在此处执行构造过程}
2、自定义构造方法:init() {} 里面的【参数不同】,则是不同的构造器!
3、形参命名和实参标签:自定义构造过程时,可以在定义中提供构造形参,指定其值的类型和名字。构造形参的功能和语法跟函数和方法的形参相同。(不过有init方法时,在类()这种初始化的时候,扣号内只能用init方法进行初始化参数)
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 }} // 参数与属性名字不一样,直接用属性名字
【可选属性类型】,可以不用 init 构造器中 赋值,自动初始化 nil
【构造过程中常量属性的赋值】直接赋值,或者在 构造器中 赋值!
4、函数形式中的参数写法都可以使用在构造方法中!
5、值类型的构造器代理
对于值类型,你可以使用 self.init 在自定义的构造器中引用相同类型中的其它构造器。并且你只能在构造器内部调用 self.init。
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) }
6、类的继承和构造过程:类里面的所有存储型属性——包括所有继承自父类的属性——都必须在构造过程中设置初始值。
7、指定构造器和便利构造器:
指定构造器:每一个类都必须至少拥有一个(相当于OC中[[NSObject alloc] init])
init(parameters) {statements}
便利构造器:是类中比较次要的、辅助型的构造器。关键词convenience修饰(相当于OC中[[NSObject alloc] initXXX]) (项目设置中 Swift Language Version 改为swift4.0 ,默认为swift5.0 ,5.0的便利构造器违法提示调用指定构造器!)
convenience init(parameters) {statements}
规则 1 指定构造器 必须调用 其直接 【父类】 的 指定构造器。
规则 2 便利构造器 必须 调用 【同类】 中定义的其它构造器。
规则 3 【便利构造器最后必须调用 —> 指定构造器。】必须是 【同类/ 父类(没有新属性)】 里有的构造器!!没有的不能调!!!
【一个更方便记忆的方法是:指定构造器必须总是向上代理!,便利构造器必须总是横向代理!】
class Food {
var name: String
init(name: String) {
self.name = name}
convenience init() {
self.init(name: "[Unnamed]")}}
7、构造器的继承和重写
跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器!!!。Swift 的这种机制可以防止一个父类的简单构造器被一个更精细的子类继承,而在用来创建子类时的新实例时没有完全或错误被初始化。
假如你希望自定义的子类中能提供一个或多个跟父类相同的构造器,你可以在子类中提供这些构造器的自定义实现。(override重写父类的构造器)
8、构造器的自动继承:子类中引入的所有新属性都提供了默认值(子类是否有新属性,以及新属性是否有默认值!)
规则 1 如果子类没有定义任何指定构造器,它将自动继承父类所有的指定构造器。
规则 2 如果子类提供了所有父类指定构造器的实现——无论是通过规则 1 继承过来的,还是提供了自定义实现——它将自动继承父类所有的便利构造器。
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name) }}
9、可失败构造器:其语法为在 init 关键字后面添加问号init?(){}。
可失败构造器会创建一个类型为自身类型的可选类型的对象。你通过 return nil 语句来表明可失败构造器在何种情况下应该 “失败”
struct Animal {
let species: Stringinit?(species: String) {
if species.isEmpty {return nil}
self.species = species}}
10、必要构造器:在类的构造器前添加 required 修饰符表明 所有该类的子类都必须实现该构造器
在子类重写父类的必要构造器时,必须在子类的构造器前也添加 required 修饰符,表明该构造器要求也应用于继承链后面的子类
十五、析构过程(OC中dealloc函数)
析构器是在实例释放发生前被自动调用的。你不能主动调用析构器。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为(比如查找一个需要被关闭的文件)。
1、deinit {// 执行析构过程}//析构器不带任何参数和圆括号
十六、可选链
1、class Person {var residence: Residence?}//residence为可选类型
class Residence {var numberOfRooms = 1}
必须这样访问! let roomCount = john.residence?.numberOfRooms//必须加问号
2、通过可选链式调用来调用方法
if john.residence?.printNumberOfRooms() != nil {print("It was possible to print the number of rooms.")}
3、通过可选链式调用访问下标:let firstRoomName = john.residence?[0].name
4、连接多层可选链式调用:let johnsStreet = john.residence?.address?.street
十七、错误处理 : 遇见 throw 关键字, 简单处理 用 try? 进行 接受处理
在 Swift 中,错误用遵循 【Error 协议的类型】 的值来表示。这个空协议表明该类型可以用于错误处理。
Swift 中有 4 种处理错误的方式。你可以把
1>函数抛出的错误传递给调用此函数的代码、
2>用 do-catch 语句处理错误、
do {
try expression statements}
catch pattern 1 { statements}
catch pattern 2 where condition { statements}
catch { statements}
3>将错误作为可选类型处理、
func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil}
4>断言此错误根本不会发生
1、用 throwing 函数传递错误,在函数声明的参数之后加上 throws 关键字
例子:func canThrowErrors() throws -> String
2、禁用错误传递:有时你知道某个 throwing 函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写 try! 来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
3、指定清理操作: defer 语句在【即将离开当前代码块时执行一系列语句】。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,或是由于诸如 return、break 的语句。例如,你可以用 defer 语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。【 defer 语句将代码的执行延迟到当前的作用域退出之前。】
func processFile(filename: String) throws {
if exists(filename) { let file = open(filename)
defer { close(file) } //这行代码会在这个函数执行完后,进行执行 defer语句!
while let line = try
file.readline() {
// 处理文件。 }
// close(file) 会在这里被调用,即作用域的最后。 }}
十八、类型转换
1、检查类型:用类型检查操作符(is)来检查一个实例是否属于特定子类型。若实例属于那个子类型,类型检查操作符返回 true,否则返回 false
if item is Movie {
movieCount += 1}
else if item is Song {
songCount += 1}
as 子类向上父类转 / as? 和 as! 父类向下转子类、或者 验证是某个类型!!
2、向下转型:某类型的一个常量或变量可能在幕后实际上属于一个子类。当确定是这种情况时,你可以尝试用类型转换操作符(as? 或 as!)向下转到它的子类型
1>用类型转换的条件形式(as?)。条件形式的类型转换总是返回一个可选值,并且若下转是不可能的,可选值将是 nil
2>只有你可以确定向下转型一定会成功时,才使用强制形式(as!)。当你试图向下转型为一个不正确的类型时,强制形式的类型转换会触发一个运行时错误。
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")}
else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")}
转换没有真的改变实例或它的值。根本的实例保持不变;只是简单地把它作为它被转换成的类型来使用。
3、Any 和 AnyObject 的类型转换(用在数组和字典中!!)
var things = [Any]()//数组中可存储任何类型
Any 可以表示任何类型,包括函数类型。
AnyObject 可以表示任何类类型的 —> 实例
十九、嵌套类型:Swift 允许你定义嵌套类型,可以在支持的类型中定义嵌套的枚举、类和结构体。类、结构体、枚举都可以在内部继续嵌套!
1、let heartsSymbol = BlackjackCard.Suit.hearts.rawValue// 通过点语法,一层一层调用
二十、extension扩展:swift中扩展可以为 类、结构体、枚举、协议 都可以进行扩展(OC扩展,与 Objective-C 分类不同的是,Swift 扩展是没有名字的。)
Swift 中的扩展可以:
1> 添加 计算型实例属性和计算型类属性 【只能是 变量 var 而且 只能有 get方法!!】——> 只有get方法!
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self } }
2>定义 【实例方法】 和 【类方法】:记住必须实现!
【swift中 扩展 不可以 添加 原类 中 已有的 属性 和 方法 !!】
3>提供新的构造器:扩展 可以给一个类添加 【新的便利构造器!】,但是它们 【不能 给类添加 新的 指定构造器或者析构器!!!】。【指定构造器和析构器必须 始终 由类 的原始实现提供】。
4>添加新的 定义下标
5>定义和使用新的嵌套类型
6>使已经存在的类型遵循(conform)一个协议 (添加实例属性、实例方法、类属性、类方法、下标)
扩展可以给一个类型添加新的功能,但是不能重写已经存在的功能(OC分类中可以重写主类已实现的方法)。!!
1、扩展的语法
extension SomeType {// 在这里给 SomeType 添加新的功能}
二十一、协议 (添加实例属性、实例方法、类属性、类方法(值类型只能使用static,类可以使用class)、构造器) (协议合成+协议拓展) 类、结构体或枚举都可以遵循协议
1、协议语法
protocol SomeProtocol {// 这里是协议的定义部分}
*2、遵循某个协议:需要在类型名称后加上协议名称,中间以冒号(:)分隔。遵循多个协议时,各协议之间用逗号(,)分隔
struct SomeStructure: FirstProtocol, AnotherProtocol {// 这里是结构体的定义部分}
//冒号后面,第一个为父类,后面是协议!!!
3、属性:协议不指定属性类型(因为跟OC类似,协议只是定义属性,具体实现放到遵守的类中实现!!!!)是存储属性还是计算属性,它只指定属性的名称和类型。此外,协议还指定属性是可读的还是可读可写的。
协议通常!用 var 关键字来声明变量属性(也可以用let),在类型声明后加上 { set get } 来表示属性是可读可写的,可读属性则用 { get } 来表示:
protocol SomeProtocol {
var mustBeSettable: Int { get set } // 协议中属性只能用var
var doesNotNeedToBeSettable: Int { get } // 只读属性
}
4、方法:不用写大括号里面的,放到遵守类中去实现!
5、异变方法要求:实现协议中的 mutating 方法时,若是类类型,则不用写 mutating 关键字。而对于结构体和枚举,则必须写 mutating 关键字。
6、构造器要求:协议可以要求遵循协议的类型实现 指定的构造器。你可以像编写普通构造器那样,在协议的定义里写下构造器的声明,但不需要写花括号和构造器的实体 ——> 【协议中可以添加 指定构造器! 和 便利构造器!】
7、协议中的 【指定构造器】、【便利构造器】在 实现类 中 ——> 必须添加 关键字:required:必须实现
8、协议作为类型 :协议作为类型使用,有时被称作「存在类型」,这个名词来自「存在着一个类型 T,该类型遵循协议 T」。(协议可以当成一个特殊的类!!!)
1>作为函数、方法或构造器中的参数类型或返回值类型 2>作为常量、变量或属性的类型 3>作为数组、字典或其他容器中的元素类型
协议是一种类型,因此协议类型的名称应与其他类型(例如 Int,Double,String)的写法相同,使用大写字母开头的驼峰式写法,例如(FullyNamed 和 RandomNumberGenerator)
【协议作为类型 ——> 理解为 一个 特殊的泛型!! 必须 使用 协议中的属性和方法 !!!! 】
9、委托代理!var delegate: DiceGameDelegate?//代理变量
10、在扩展里添加协议遵循——> 让原类 间接遵守这个协议(这个不同OC—>swift可以扩展遵守代理)(让系统类遵守代理!!)
extension Dice: TextRepresentable {//扩展遵守代理
var textualDescription: String {return "A \(sides)-sided dice"}}
11、协议类型的集合:遵守该协议的元素集合
let things: [TextRepresentable] = [game, d12, simonTheHamster]
12、协议的继承:——> 【给原有的协议 添加 新的协议!】
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {// 这里是协议的定义部分}
13、类专属的协议:你通过添加 AnyObject 关键字到协议的继承列表,就可以限制协议只能被类类型采纳(以及非结构体或者非枚举的类型)。
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// 这里是类专属协议的定义部分}//Any的话,结构体也可以遵守该代理,AnyObject只能类遵守协议
14、协议合成:协议组合使用 SomeProtocol & AnotherProtocol 的形式。你可以列举任意数量的协议,用和符号(&)分开。
protocol Named {var name: String { get }}//Named协议
protocol Aged {var age: Int { get }}//Aged协议
struct Person: Named, Aged {var name: Stringvar age: Int}
func wishHappyBirthday(to celebrator: Named & Aged) {print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")}//参数必须同时遵守 Named和Aged代理的对象 也可以是 NSObject & 代理——>代理必须是NSObject的类及子类 并且 遵守 代理
15、判断对象是否遵守某个协议 / 获取对象遵守的协议
is 用来检查实例是否遵循某个协议,若遵循则返回 true,否则返回 false;
as? 返回一个可选值,当实例遵循某个协议时,返回类型为协议类型的可选值,否则返回 nil;
as! 将实例强制向下转换到某个协议类型,如果强转失败,将触发运行时错误。
let objectWithArea = object is HasArea //判断是否遵守
let objectWithArea = object as? HasArea//获取遵守的协议
16、optional关键字 可选的协议要求:标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议。协议和可选要求都必须带上 @objc 属性。
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }}//这个协议只能OC类遵守
17、协议扩展:协议也可以进行扩展,协议的扩展默认可以直接进行实现该方法!!!
extension protocal {
func randomBool() -> Bool {return random() > 0.5}}
二十二、泛型(任意类型)(函数、类、协议 、下标 都可以添加泛型)
1、泛型函数(用T来代替具体类型,并且函数名后添加 <T>)占位符类型名
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = aa = bb = temporaryA}//函数名 T 和 参数 T
2、泛型类型:Element这个词自己随便定义!(类名后面添加<T>)
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {items.append(item)}
mutating func pop() -> Element {return items.removeLast()}}
使用样例:
var stackOfStrings = Stack<String>()//传入String类型
stackOfStrings.push("uno")
3、泛型扩展
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]}}
4、类型约束:在一个类型参数名后面放置一个类名或者协议名,并用冒号进行分隔,来定义类型约束。下面将展示泛型函数约束的基本语法(与泛型类型的语法相同)
例子:
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {// 这里是泛型函数的函数体部分}
5、协议中泛型:关联类型为 协议 中的某个类型提供了一个占位符名称,其代表的实际类型在协议被遵循时才会被指定。关联类型通过 associatedtype 关键字来指定。
protocol Container {
associatedtype Item //自定义泛型
associatedtype Item: Equatable//协议泛型添加约束类型
mutating func append(_ item: Item)var count: Int { get }subscript(i: Int) -> Item { get }}//凡是遵守这个代理的,实现这三个方法,参数类型可以随便!
6、泛型 Where 语句(where后面是条件!!)
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool where C1.Item == C2.Item, C1.Item: Equatable {}
7、泛型下标
extension Container {
subscript<Indices: Sequence>(indices: Indices) -> [Item]where Indices.Iterator.Element == Int {
var result = [Item]()for index in indices {result.append(self[index])}return result}}
二十三、不透明类型 ——> 泛型函数 返回的类型 —> some + 协议 类型!!
https://zhuanlan.zhihu.com/p/76490173
例子:func makeTrapezoid() -> some Shape { return demo } // 不透明类型 语法 :some + Type
不透明类型和协议类型的区别 : 虽然使用不透明类型作为函数返回值,看起来和返回协议类型非常相似,但这两者有一个主要区别,就在于【是否需要保证类型一致性】。【一个不透明类型只能对应一个具体的类型】 ,即便函数调用者并不能知道是哪一种类型;【协议类型可以同时对应多个类型 】,只要它们都遵循同一协议。总的来说,协议类型更具灵活性,底层类型可以存储更多样的值,而不透明类型对这些底层类型有更强的限定。
不透明类型 :(是一个被约束加了条件的类型——> 遵守该协议、父类的子类!)
不透明类型 定义了 遵循某个协议 或者 合成协议的类型,但 不需要指明底层 的具体类型。
不透明类型可以作为函数或下标的返回值,亦或是属性的类型使用。不透明类型不能作为元组类型的一部分或范型类型使用,比如数组元素类型或者可选值的包装类型。
不透明类型的形式如下:some constraint ——>constraint 可以是类类型,协议类型,协议组合类型或者 Any。
值只有当它遵循该协议或者组合协议,或者从该类继承的时候,才能作为这个不透明类型的实例使用。
举个例子,函数 someFunction<T>() 可以返回类型 T 或者 Dictionary<String,T> 的值。不透明类型语法opaque-type 不透明类型 → some type
无主引用和隐式解包可选值属性二十四、自动引用计数:Swift 使用 【自动引用计数(ARC)】机制来跟踪和管理你的应用程序的内存。引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型。
1、类实例之间的循环强引用:通过定义类之间的关系为 弱引用或无主引用,来替代强引用,从而解决循环强引用的问题
2、解决实例之间的循环强引用:Swift 提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)// 弱引用修饰的变量必须是var!!!因为最后会被置 nil! 当 ARC 设置弱引用为 nil 时,属性观察不会被触发。(第一次赋值,和最后置nil 都不会被监测)
1>weak 弱引用 —> 修饰变量 —> weak var tenant: Person?
2>unowned 无主引用—>修饰常量 —>unowned let customer: Customer
【当其他的实例有更短的生命周期时,使用弱引用,也就是说,当其他实例析构在先时。在上面公寓的例子中,很显然一个公寓在它的生命周期内会在某个时间段没有它的主人,所以一个弱引用就加在公寓类里面,避免循环引用。相比之下,当其他实例有相同的或者更长生命周期时,请使用无主引用。】
注意:使用无主引用,你必须确保引用始终指向一个未销毁的实例。如果你试图在实例被销毁后,访问该实例的无主引用,会触发运行时错误。
【Person 和 Apartment 的例子展示了 两个属性的值都允许为 nil —> var修饰 ,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
Customer 和 CreditCard 的例子展示了一个 属性的值允许为 nil,而另一个属性的值不允许为 nil —> 一个var 一个 let,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。】
3、闭包的循环强引用——>定义捕获列表
lazy var someClosure = {
[unowned self, weak delegate = self.delegate]//这个必须在闭包里第一行! 这里的self 用unowned 其他变量用 weak
(index: Int, stringToProcess: String) -> String in //in关键字必须带上!
// 这里是闭包的函数体}
【Swift 有如下要求:只要在闭包内使用 self 的成员,就要用 self.someProperty 或者 self.someMethod()(而不只是 someProperty 或 someMethod())。这提醒你可能会一不小心就捕获了 self。】
4、弱引用和无主引用
弱引用:可能为nil空值
无主引用:一定不为空值(如果被捕获的引用绝对不会变为 nil,应该用无主引用)
二十五、内存安全
1、In-Out 参数的访问冲突
var stepSize = 1
func increment(_ number: inout Int) {
number += stepSize} // number 和 stepSize 是同一个变量,同时操作 读 和 写 ,会冲突!
increment(&stepSize)// 错误:stepSize 访问冲突
解决这个冲突的一种方式,是显示拷贝一份 stepSize :
// 显式拷贝var copyOfStepSize = stepSize
increment(©OfStepSize)
// 更新原来的值
stepSize = copyOfStepSize// stepSize 现在的值是 2
2、一个结构体的 mutating 方法会在调用期间对 self 进行写访问
3、属性的访问冲突
根据业务逻辑去分析,该用 in-out 获取指针传递 还是 值传递!
二十六、访问控制:你可以明确地给 单个类型(类、结构体、枚举)设置访问级别,也可以给这些类型的属性、方法、构造器、下标等设置访问级别。协议也可以被限定在一定访问级别的范围内使用,包括协议里的全局常量、变量和函数。
1、Swift 中的访问控制模型基于 模块 和 源文件这两个概念。
2、访问级别:Swift 中的访问级别遵循一个基本原则:实体不能定义在具有更低访问级别(更严格)的实体中。( 高级别 不能定义在 低级别 中)
1>open 和 public 级别可以让实体被同一模块源文件中的所有实体访问。(框架SDK开发才需要使用这个级别!!)
Open 的类:可以在框架内部和外部,被访问、继承、重写
Public的类:在框架内部可以 被访问、继承、重写。外部:只能访问!
2>internal 级别让实体被同一模块源文件中的任何实体访问,但是不能被模块外的实体访问。默认访问级别!!!(APP内创建的默认都是这个级别!!除非SDK框架开发使用open和public),只要不写级别,统统都是internal!!
3>fileprivate 限制实体 只能在其定义的文件内部访问。
4>private 限制实体 只能在其定义的作用域,以及同一文件内的 extension 访问。
public class SomePublicClass { // 显式 public 类
public var somePublicProperty = 0 // 显式 public 类成员
var someInternalProperty = 0 // 隐式 internal 类成员
fileprivate func someFilePrivateMethod() {} // 显式 fileprivate 类成员
private func somePrivateMethod() {} // 显式 private 类成员}
3、元祖访问级别:由元组中元素的最低访问级别来决定的,不能被显示指定。
4、函数类型:参数类型 或 返回类型 的 最低访问级别来决定
5、枚举类型:枚举成员的访问级别和该枚举类型相同,枚举成员不能单独指定不同的访问级别。
6、子类:可以继承同一模块中的所有有访问权限的类,也可以继承不同模块中被 open 修饰的类。一个子类的访问级别不得高于父类的访问级别。
在不同模块中,你可以 重写类中被 open 修饰的成员。
7、常量、变量、属性、下标:不能拥有比它们的类型更高的访问级别
8、构造器:自定义构造器的访问级别可以低于或等于其所属类型的访问级别。唯一的例外是 必要构造器,它的访问级别 必须!! 和所属类型的访问级别相同。
9、协议:协议中 的 每个 方法或属性 都 必须 具有和该协议相同的访问级别。你不能将协议中的方法或属性设置为其他访问级别
协议继承:新协议拥有的访问级别 最高也只能和被继承协议的访问级别相同。(新协议访问级别 <= 父类协议!!!)
协议遵循:一个类型可以遵循比它级别更低的协议。(遵守类的协议方法实现的级别跟协议级别一样)
10、Extension 可以在访问级别允许的情况下对类、结构体、枚举进行扩展。Extension 的新增成员具有和原始类型成员一致的访问级别。
11、泛型:泛型类型或泛型函数的 访问级别 取决于 泛型类型或泛型函数本身的访问级别,还需结合类型参数的类型约束的访问级别,根据这些访问级别中的最低访问级别来确定。
二十七、高级运算符
1、位运算
2、溢出运算:&
溢出加法 &+
溢出减法 &-
溢出乘法 &*
3、优先级和结合性(与C一样)
4、运算符函数
不能对默认的赋值运算符(=)进行重载。只有复合赋值运算符可以被重载。同样地,也无法对三元条件运算符 (a ? b : c) 进行重载。
5、自定义运算符:新的运算符要使用 operator 关键字在全局作用域内进行定义,同时还要指定 prefix、infix 或者 postfix 修饰符:prefix operator +++
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vectorreturn vector}}
网友评论