Swift高级运算符(Advanced Operators)

最后更新于:2022-04-01 14:34:39

## 按位运算符 ### ~ 1变0,0变1。 ![~](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-01_574e9af923b08.jpg "") ~~~ let initialBits: UInt8 = 0b00001111 let invertedBits = ~initialBits // equals 11110000 ~~~ ### $ 全1得1,其他为0 ![$](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-01_574e9af93e555.jpg "") ~~~ let firstSixBits: UInt8 = 0b11111100 let lastSixBits: UInt8 = 0b00111111 let middleFourBits = firstSixBits & lastSixBits // equals 00111100 ~~~ ### | 有1得1,其他为0 ![|](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-01_574e9af967c22.jpg "") ~~~ let someBits: UInt8 = 0b10110010 let moreBits: UInt8 = 0b01011110 let combinedbits = someBits | moreBits // equals 11111110 ~~~ ### ^ 相异为1,相同为0 ![^](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-01_574e9af97cac0.jpg "") ~~~ let firstBits: UInt8 = 0b00010100 let otherBits: UInt8 = 0b00000101 let outputBits = firstBits ^ otherBits // equals 00010001 ~~~ ### <<和>> <<整体左移,右边填0; >> 整体右移,左边填0。 ![<<和>>](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-01_574e9af99016a.jpg "") ~~~ let shiftBits: UInt8 = 4 // 00000100 in binary shiftBits << 1 // 00001000 shiftBits << 2 // 00010000 shiftBits << 5 // 10000000 shiftBits << 6 // 00000000 shiftBits >> 2 // 00000001 ~~~ ## 算子函数 上面讲解的都是简单的运算符,下面的是为对象添加运算符,使之可计算。 ### 符号在中间 ~~~ struct Vector2D { var x = 0.0, y = 0.0 } func + (left: Vector2D, right: Vector2D) -> Vector2D { return Vector2D(x: left.x + right.x, y: left.y + right.y) } let vector = Vector2D(x: 3.0, y: 1.0) let anotherVector = Vector2D(x: 2.0, y: 4.0) let combinedVector = vector + anotherVector // combinedVector is a Vector2D instance with values of (5.0, 5.0) ~~~ ### 前缀和后缀 前缀关键字prefix 后缀关键字postfix ~~~ prefix func - (vector: Vector2D) -> Vector2D { return Vector2D(x: -vector.x, y: -vector.y) } let positive = Vector2D(x: 3.0, y: 4.0) let negative = -positive // negative is a Vector2D instance with values of (-3.0, -4.0) let alsoPositive = -negative // alsoPositive is a Vector2D instance with values of (3.0, 4.0) ~~~ ### 复合赋值运算符 这里用+=举例。 ~~~ func += (inout left: Vector2D, right: Vector2D) { left = left + right } var original = Vector2D(x: 1.0, y: 2.0) let vectorToAdd = Vector2D(x: 3.0, y: 4.0) original += vectorToAdd // original now has values of (4.0, 6.0) ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-11-1 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Classes and Structures总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift访问控制(Access Control)

最后更新于:2022-04-01 14:34:36

当你在Swift编写一个类时,默认其中任何属性,方法都能被外部访问的。有的时候我们不希望属性或方法被外部访问,希望私有化。 在swfit用于访问控制的有三个关键字。 - public:公共访问,允许在任何源文件中使用其定义模块。如你使用XCTest测试某个类时,就需要在类前添加public。 - internal:swift默认访问控制,允许在项目内访问。 - private:私人访问,只能在当前类中访问。如果是在class前添加,则只能是当前文件访问。 举例说明: ~~~ public class SomePublicClass { // 明确 public class public var somePublicProperty = 0 // 明确 public class 成员 var someInternalProperty = 0 // 默认 internal class 成员 private func somePrivateMethod() {} // 明确 private class 成员 } class SomeInternalClass { // 默认 internal class var someInternalProperty = 0 // 默认 internal class 成员 private func somePrivateMethod() {} // 明确 private class 成员 } private class SomePrivateClass { // 明确 private class var somePrivateProperty = 0 // 默认 private class 成员 func somePrivateMethod() {} // 默认 private class 成员 } ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-11-1 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Classes and Structures总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift协议(Protocols)

最后更新于:2022-04-01 14:34:34

协议主要为一个特定的任务和功能定义一个方法、属性和其他要求,你也可以理解协议就是一种要遵循的规范。 学过设计模式的,都知道工厂模式,如果你不知道可以查阅我的博文《[23设计模式之工厂方法(FactoryMethod)](http://blog.csdn.net/y550918116j/article/details/48596527)》,工厂模式就是一种协议的体现。在Java中,是用接口定义协议的;在OC中,主要用于代理。 除了已有的协议,你还可以像扩展类一样扩展协议。这些扩展的协议可以实现也可以直接使用。 ## 语法 协议语法使用了关键字protocol,和其他类型不同的是,它是规范的指定者,无须去实现。下面是一个空得协议。 ~~~ protocol SomeProtocol { // protocol definition goes here } ~~~ 协议可以被结构体继承, ~~~ struct SomeStructure: FirstProtocol, AnotherProtocol { // structure definition goes here } ~~~ 协议也可以被类继承,当然这是它最主要的功能。 ~~~ class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol { // class definition goes here } ~~~ ### 协议 这里我们设计一个协议,并实现一个简单方法。我们在这里使用了一个简单方法。 ~~~ protocol YJSomeProtocol { func test() } ~~~ 编写一个类去实现这个协议。 ~~~ class YJSomeClass: YJSomeProtocol { func test() { print(__FUNCTION__) } } let yjs = YJSomeClass() yjs.test() ~~~ 如果你想在类型中使用类型方法,可以在func前加static。 ~~~ static func test() ~~~ 你还可以让该协议只能由class去实现,只需让协议去继承class ~~~ protocol YJSomeProtocol: class ~~~ ## 代理 在Swift中有各种各样的代理,这些代理都是通过协议来规范的,想知道更多关于代理模式的实现详见我的博文《[23设计模式之代理模式(Proxy)](http://blog.csdn.net/y550918116j/article/details/48605595)》。 ## 继承 协议也支持继承。 ~~~ protocol YJAnotherProtocol: YJSomeProtocol { // 协议可继承 } ~~~ 继承的好处就是我们可以在一些公共的协议上,添加我们的协议,使之具有延伸性。 ## 可选 在swift中又可选链,也就是‘?’和’!’。在这里我们考虑到这种情况,有一个协议里面有5个方法,其中两个方法是必须的,其他3个方法,继承的类不必强制去实现。 在OC中就有这种机制@optional,在swift也可以这样使用,但是@optional是OC 的特性。因此,我们想使用这种特性的时候,就需要借助@objc。@objc可以理解为swift和oc的桥梁。 ~~~ @objc protocol YJSomeProtocol:class { // class代表只用类才能实现这个协议 func test() // @objc:OC特性,代表可以使用optional特性。optional可选的方法 optional func testOptional() } ~~~ 但是这样对实现类有一定的影响,因为这个特性是OC的。而OC中所有的类都是继承NSObject,故实现这个协议的类也要继承NSObject。 ~~~ class YJSomeClass:NSObject, YJSomeProtocol { func test() { print(__FUNCTION__) } } ~~~ ## 扩展 协议也支持扩展extension,这也相当于可选。只是在扩展的协议中,需要实现方法。 ~~~ extension YJSomeProtocol { func testExtension() { print(__FUNCTION__) } } ~~~ 实现类可以实现这个方法,也可以不实现这个方法。 ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-11-2 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Protocols总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift类型选择(Type Casting)

最后更新于:2022-04-01 14:34:32

在Swift开发过程中,我们会遇到以下情况:判断某个实例是那个类生成的;将子类转换为父类;想让一个变量可以为任何类型(值对象、引用对象、方法)。。。 Swift也能处理这些情况,需要使用的关键字:类型判断is、类型转换as、属性声明AnyObject和Any。 在介绍这四个关键字的使用前,先构建类MediaItem、Movie和Song。 ~~~ class MediaItem { } class Movie: MediaItem { } class Song: MediaItem { } ~~~ 其中Movie和Song都继承MediaItem。 ## Is is主要用于类型判断,如我们判断某个实例是那个类的子类,或是那个类生成的。 ~~~ let array = [Song(), Movie()] // is测试,类型判断 for item in array { if item is Movie { print("Movie构建") } else if item is Song { print("Song构建") } } ~~~ ## As as主要用于类型转换,可将一个类转换为另一个类。as后可以跟’?’或’!’。 - as?:预转换,转换失败时,返回nil。 - as!:强转换,转换失败时,程序崩溃。 ~~~ for item in array { if let movie = item as? Movie { print("可转换为Movie: '\(movie)'") } else if let song = item as? Song { print("可转换为Song: '\(song)'") } // 强转换,失败时,程序崩溃 let movie = item as! Movie } ~~~ ## AnyObject 使用AnyObject声明的常量(变量)可以是值对象或引用对象。 ~~~ let someObjects: [AnyObject] = [Movie(), 1, "33"] ~~~ ## Any Any和AnyObject具有相同的特性,只是Any还可以代表方法和闭包。 ~~~ var things = [Any]() things.append(0) // 值类型 things.append(Movie()) // 引用类型 things.append({ (name: String) -> String in "Hello, \(name)" }) // 闭包 ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-11-1 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Type Casting总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift错误处理(Error Handling)

最后更新于:2022-04-01 14:34:30

错误处理是在程序中响应错误和处理错误恢复程序正常运行的过程。Swift提供了抛出、捕获、传播和操作可恢复的过程。 有的操作不是每次都能执行成功的,而可选链是用来判断有没有值,但操作失败时,无法知道失败的原因。错误处理就是帮助你了解失败的原因,方便你做出相应的反应。 比如我们在网络传输时,会遇到各种各样的突发情况导致数据传输失败。这时,通过错误处理机制,就能有效的提示用户当前是因为那种错误导致网络出错。 ## 声明错误 声明错误用到了枚举,其中枚举实现ErrorType协议。这里我们使用枚举声明一个简单的错误。 ~~~ /// 错误枚举,需ErrorType协议 enum ErrorEnum: ErrorType { case Default // 普通错误 case Other(tag: Int) // 高级错误,可携带数据 } ~~~ 这里面有两个错误Default和Other,其中Other可携带数据。 ## 抛出错误 我们声明了错误后,就在类中抛出错误,抛出错误只需要使用throw。 ~~~ throw ErrorEnum.Other(tag: 5) ~~~ 区别常见的方法,能够抛出错误的方法体,需要添加throw。 ~~~ // 可抛出错误的方法体 func canThrowErrors() throws -> String // 常见方法体 func cannotThrowErrors() -> String ~~~ 这里我们创建一个类SomeClass,在类SomeClass中有一个能抛错的方法体。 ~~~ class SomeClass { func canThrowErrors(str: String) throws { // 当str不为Default时,输出错误 guard str == "Default" else { throw ErrorEnum.Default } // 当str部位Other时输出错误 guard str == "Other" else { throw ErrorEnum.Other(tag: 5) } } } ~~~ 这里用到了guard校验,然后抛出错误。如果你不知道guard语法,可查阅我的博文《[Swift控制流](http://blog.csdn.net/y550918116j/article/details/49428647)》。 ##处理错误 在Swift捕获错误使用do-catch语法,如下所示: ![错误处理](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-01_574e9af9024ab.jpg "") 如图所示,你要执行的代码放在do的括号内,执行可抛出错误的代码使用try;catch就是捕获的意思,会执行每一个catch,匹配成功后,则执行内部代码,和switch中的case很像;where代表条件匹配,在错误匹配成功后,还要匹配where后的语句才会执行内部代码。如下所示: ~~~ do { try sClass.canThrowErrors("Default") try sClass.canThrowErrors("Other") } catch ErrorEnum.Default { print("默认错误") } catch ErrorEnum.Other(let tag) where tag == 5 { print("错误代码:\(tag)") } catch ErrorEnum.Other(let tag) { print("其他错误:\(tag)") } catch { // 在捕获中,隐式携带error错误。 print("未知错误:\(error)") } ~~~ 其中第一个try抛错后,则不会执行第二个try。 如果你不想写这么繁琐,Swift也支持只写一行代码,使用’!’。如: ~~~ try! sClass.canThrowErrors("Default") ~~~ 请慎重使用’!’,容易引起程序闪退。 ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-11-1 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Error Handling总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift可选链(Optional Chaining)

最后更新于:2022-04-01 14:34:27

了解过设计模式的人都知道责任链模式(如果你不知道什么是责任链模式,可以阅读我的博文《[23设计模式之责任链模式 (COR)](http://blog.csdn.net/y550918116j/article/details/48596903)》),在OC的手势响应链也是基于这种模式开发的。 责任链模式的核心可以理解为 ~~~ if { } else if { } else { } ~~~ 如果每一次都这样写,代码就特别多,也不美观。 Swift考虑到这种情况设计了可选链,可选链的核心是两个操作符号: - **?**:当’?’前有值时,执行’?’后面代码,为nil时不执行’?’后面的代码;如果是赋值的时候使用,则意味着这个常量、变量可能为有值或nil。 - **!**:不管’!’前有值还是nil,都执行‘!’后面的代码,你可以理解为强制调用;如果是赋值的时候使用,则意味着这个常量、变量一定有值。 ‘!’要慎重使用,不然会引起程序崩溃,app闪退。 这里设计两个类: ~~~ class Residence { var numberOfRooms = 1 } class Person { // 可选属性,可能为nil或Residence类 var residence: Residence? } ~~~ 在Person中有个引用类型的属性residence,这个属性可以是nil也可以是Residence类,这就是一个可选属性。 下面给大家介绍可选链的使用。 ### 可选获得 ~~~ let john = Person() john.residence = Residence() // 可选获得 var roomCount = john.residence?.numberOfRooms ~~~ ### 强制获得 如果你已经确定这个属性有值是,你可以使用‘!’强制获得。 ~~~ // 强制获得 roomCount = john.residence!.numberOfRooms print(roomCount) // Optional(1) Optional代表可选 ~~~ ### if获得 在上面两种方式都比较麻烦,第一种方式,当你去使用的时候,你还需要去if判断;第二种方式,不是每一次都能强制调用成功的,当为nil时强制调用,会引起程序崩溃。处于这种情况,我们可以结合if使用更优雅的方式获得。 ~~~ // if获得 if let roomCount = john.residence?.numberOfRooms { print(roomCount) // 1 } ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-11-1 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Optional Chaining总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift销毁(Deinitialization)

最后更新于:2022-04-01 14:34:25

在Swift中,也支持ARC机制,也就是内存自动回收机制。 在上一篇博文我们讲了《[Swift初始化(Initialization)](http://blog.csdn.net/y550918116j/article/details/49535219)》,既然有了初始化的方法,Swift也提供了销毁的通知方法。 deinit只能在引用类型中使用,也就是只能在类中使用。 ~~~ deinit { // perform the deinitialization } ~~~ 下面就用一个例子给大家介绍销毁是怎么工作的。 ~~~ class SomeClass { // MARK: 类销毁时,通知此方法 deinit { print("销毁") } } var sClass:SomeClass? = SomeClass() sClass = nil // print "销毁" ~~~ 我们设置变量sClass为可选的SomeClass类,当我们将sClass设为nil时,当然你也可以不执行这行代码,swift会自动回收sClass分配的内存空间。 这里我们人为的调用`sClass = nil`后,Swift开始销毁sClass,销毁完毕通知SomeClass类中的deinit方法体,最后完成内存回收。 ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-31 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Deinitialization总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift初始化(Initialization)

最后更新于:2022-04-01 14:34:23

实例的初始化是准备一个类、结构体或枚举的实例以便使用的过程。初始化包括设置一个实例存储属性的初始值,以及其他相关设置。 一个类、结构体或枚举能定义一个初始化方法来设置它的特性,用来确保它的所有属性都是有效的初始值。 通过调用类、结构或枚举提供的初始化方法可以执行实例的初始化过程。 > 构造初始化类、结构体和枚举的初始化方法所使用到的关键字是init。 ## 初始化 ### 初始化结构体 下面我们定义一个结构体Size,并添加两个属性wdith和height。 ~~~ struct Size { /// 宽 var width = 0.0 /// 高 var height = 0.0 } ~~~ 使用结构体自带的初始化方法实例化结构体: ~~~ // 默认初始化,使用的是init()构造方法 var size = Size() // 结构体自带根据成员初始化结构体的功能 size = Size(width: 10, height: 10) ~~~ 接下来我们使用自定义的初始化功能初始化结构体,这里我们创建一个结构体Point,并添加两个属性x、y以及相关初始化方法。 ~~~ struct Point { var x = 0.0, y = 0.0 // MARK: 默认初始化 init() { } // MARK: 通过成员初始化结构体 init(x:Double, y:Double) { self.x = x self.y = y } } // 自定义初始化 var point = Point() // 调用inin()方法 point = Point(x: 10, y: 10) // 调用init(x: Double, y:Double)方法 ~~~ 这就是仿照系统自带的方法实现。但是当你在结构体自定义初始化方法时,则无法在外部使用系统自带的初始化方法。 ### 初始化枚举 枚举和结构体一样也有一些系统自带的初始化方法,和结构体不同的是,你在枚举中自定义方法后,还是可以使用系统自带的初始化方法。下面我们以一个CompassPoint枚举举例,它的内部是String类型。 ~~~ enum CompassPoint:String { case North, South, East, West init(symbol: String) { switch symbol { case "North": self = .North case "South": self = .South case "East": self = .East default: self = .West } } } // 直接取值 var compassPoint = CompassPoint.West // 通过原始值初始化 compassPoint = CompassPoint(rawValue: "North")! // 通过自定义的初始化方法 compassPoint = CompassPoint(symbol: "North") ~~~ 在这里为大家介绍了三种初始化方法:直接取值、通过原始值和通过自定义的方法获取枚举。 ### 初始化类 在Swift中,类是一个特殊的类型,它是引用类型,并且可继承。这里定义两个类:基类BaseClass和子类SubClass。 ~~~ class BaseClass { var name: String? // 可选属性类型,可能为String或nil init() { print("Food:init()") } convenience init(name: String) { self.init() self.name = name } // MARK: - 省略外部参数 convenience init(_ subName: String) { self.init(name: subName) } } ~~~ ### 子类调用父类初始化方法 在子类的初始化方法中想调用父类的初始化方法,只需使用关键字super。 ~~~ class SubClass: BaseClass { override init() { super.init() // 实现父类的init()方法 } } ~~~ ### 默认初始化 类和结构体一样也有默认初始化,不同的是它只有一个默认初始化方法。 ~~~ var user = SubClass() ~~~ ### 自定义初始化 有的时候我们希望定制一些初始化方法方便外部调用,这里我们定制了初始化name的方法`init(name: String)`。 正如大家看见的在这个方法中我们使用了关键字convenience。convenience的作用就是当我们在一个初始化方法体内想调用另一个初始化方法体,则需要在func前加convenience。如我们在`init(name: String)`中使用了初始化方法`self.init()` 调用如下: ~~~ // 通过属性初始化 user = SubClass(name: "阳君") ~~~ ### 省略外部参数 在外部使用自定义的初始化方法时,需要使用参数名,如果你不想使用参数名,也可以在初始化的方法中参数名前加`_`,如 ~~~ convenience init(_ subName: String) { self.init(name: subName) } // 使用init(_ name: String)初始化 user = SubClass("阳君") ~~~ ## 初始化失败 在开发过程中,外部使用初始化方法时,由于传入的参数不符合规范,我们需要返回nil,也就是初始化失败。类、枚举和结构体都可以初始化失败。构造可返回nil的初始化方法很简单,只需要在nil后添加“?”。 ### 结构体初始化失败 ~~~ struct Animal { let species: String init?(species: String) { if species.isEmpty { return nil } self.species = species } } let someCreature = Animal(species: "Giraffe") // 运用可选链判断 if let giraffe = someCreature { print("\(giraffe.species)") // Giraffe } ~~~ 使用Animal的init?(species: String)方法可能返回nil,也就是说someCreature为Animal或nil,接下来我们用了可选链的判断方式。当giraff不为nil时输出species属性。 ### 初始化枚举失败 ~~~ enum TemperatureUnit:Character { case Kelvin = "K", Celsius = "C", Fahrenheit = "F" init?(symbol: Character) { switch symbol { case "K": self = .Kelvin case "C": self = .Celsius case "F": self = .Fahrenheit default: return nil } } } // 通过自定义方法初始化 var unit = TemperatureUnit(symbol: "F") print(unit) // Fahrenheit unit = TemperatureUnit(symbol: "X") print(unit) // nil // 通过原始值初始化 unit = TemperatureUnit(rawValue: "F") print(unit) // Fahrenheit unit = TemperatureUnit(rawValue: "X") print(unit) // nil ~~~ 使用枚举初始化失败有两种方式:一种是自定义的可返回nil方法;一种是使用原始值获取枚举。 ### 类初始化失败 ~~~ class Product { var name: String? init() {} init?(name: String) { self.name = name if name.isEmpty { return nil } } } // 可选链操作,当bowTie为真时,执行内部代码 if let qq = Product(name: "937447974") { print(qq.name) // 937447974 } ~~~ ## 必须初始化 有的时候我们会碰到这样的场景,如父类提供了初始化方法,当子类继承时,希望子类也强制性的初始化这个方法。这里就用到了关键字required。 ~~~ class SomeClass { required init() { // required:子类要调用此方法,必须继承实现 } } class SomeSubclass: SomeClass { required init() { } init(required:String) { super.init() // 这里调用父类的init()方法,当前类必须实现init()方法 } } ~~~ ## 使用闭包或函数设置默认属性 在类中我们可以设置属性的默认值,我们还可以通过闭包或函数设置属性的默认值。 ~~~ class SomeClass { let someProperty: String = { // create a default value for someProperty inside this closure // someValue must be of the same type as SomeType return "someValue" }() } let c = SomeClass() print(c.someProperty) // someValue ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-31 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Initialization总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift继承(Inheritance)

最后更新于:2022-04-01 14:34:21

玩过其他开发语言的小伙伴都知道继承,也就是子类继承父类的特性。这在开发过程中非常有用,可以节省大量工作量。 ## 声明基类 下面我们声明一个常见的基类Base,有两个属性(count、description)和一个方法(inherited),其中description是只读的。 ~~~ /// 基类 class Base { var count = 0.0 var description: String { return "count:\(count)" } // MARK: 可继承 func inherited() { } } ~~~ ## 子类化 子类继承基类很简单,声明如下,子类和父类间用“:”隔开,如果有多个父类,父类间用“,”隔开。 ~~~ /// 子类 class Subclass: Base { } ~~~ ## 重写 在子类中可以实现父类的所有特性,但是有的时候我们想扩展属性或方法。这个时候就需要用到关键字Overriding。 下面让子类重写父类的属性和方法。 ~~~ class Subclass: Base { // 继承的属性和方法前都有override override var count:Double { didSet { print("\(__FUNCTION__)") } } override var description: String { return "\(__FUNCTION__)" + super.description } override func inherited() { print("\(__FUNCTION__)") } } ~~~ 可以在属性和方法前使用override,表示这个属性和方法是继承了父类。如果想调用父类的属性或方法,只需要使用super后面跟你想使用的属性和方法。你可以理解为self代表当前类,super代表父类。 ## 防止重写 有的时候我们不希望其他人通过继承改写我们的特性,希望它是不可重写的。这里可以使用关键字final,如下所示: - final var/let:防止常量或变量被重写; - final func:防止实例方法被重写; - final class func:防止类型方法被重写; - final subscript:防止下标方法被重写; - final class:防止当前类被继承。 这里以实例方法举例,在Base中添加如下方法: ~~~ final func preventing() { // 如果不想子类继承,可在类、属性或方法前添加final } ~~~ 则子类Subclass不可重写此方法。 ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-28 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Inheritance总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift下标(Subscripts)

最后更新于:2022-04-01 14:34:18

在Swift中,类、结构体和枚举都是支持下标语法的。 什么是下标语法?使用过数组、字典的朋友都见过array[index]。通过这样的方式可以设置数据和取数,会很方便也很简洁。你可以给一个类定义多个下标,也可以在一个下标中定义一个或多个参数。 下标的关键字是subscript,常用格式如下: ~~~ subscript(index: Int) -> Int { get { // return an appropriate subscript value here } set(newValue) { // perform a suitable setting action here } } ~~~ 下面我们以数组为例,给大家介绍下标的创建和使用。 ~~~ /// array结构体 struct TestArray { /// 内部数组 var array = Array<Int>() // MARK: 下标使用 subscript(index: Int) -> Int { get { assert(index < array.count, "下标越界") return array[index] } set { while array.count <= index { array.append(0) } array[index] = newValue } } } var array = TestArray() array[3] = 4; // 通过下标设置值 print("\(array[3])") // 4 print("\(array[4])") // 程序停止 ~~~ ### 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-30 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Subscripts总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift方法(Methods)

最后更新于:2022-04-01 14:34:16

方法是与特定类型关联的函数。在类、结构体和枚举中都可以定义方法。方法可以是实例方法,也可以是类型方法。 ## 实例方法 实例方法是属于某一特定类、结构体或枚举的函数。它们提供访问和修改实例属性的方法,或者提供与实例相关的功能。实例方法和函数的功能相同,具有完全相同的语法。 在实例方法的大括号内,可以访问局部变量和全局变量。实例方法同样也可以调用其他实例方法。 ### 局部变量和全局变量 ~~~ class Counter { var count: Int = 0 func incrementBy(amount: Int, numberOfTimes: Int) { count += amount * numberOfTimes } } let counter = Counter() counter.incrementBy(5, numberOfTimes: 3) print("\(counter.count)") // 15 ~~~ 如上所示:类Counter中有一个变量count,和一个实例方法incrementBy。在incrementBy方法内可以访问全局变量count。 ### 修改外部参数名称 如上面的例子,在外部调用方法时使用`incrementBy(5, numberOfTimes: 3)`,如果你觉得这样太麻烦,我们还可以省略numberOfTimes这个名称,只需参数名前加下划线。即修改方法体为`func incrementBy(amount: Int, _ numberOfTimes: Int)`,此时的外部访问变为`counter.incrementBy(5, 3)`。 ### Self属性 在Swift中也可以使用self访问全局属性或全局方法。 ~~~ struct Point { var x = 0.0, y = 0.0 func isToTheRightOfX(x: Double) -> Bool { // 这里有内部和外部属性 return self.x > x } } let somePoint = Point(x: 4.0, y: 5.0) print("\(somePoint.isToTheRightOfX(1.0))") // true ~~~ ### 在实例方法中修改值类型 在swift中,不但可以给类添加方法,还可以在结构体和枚举中添加方法。 ~~~ struct Point { var x = 0.0, y = 0.0 mutating func moveByX(deltaX: Double, y deltaY: Double) { x += deltaX y += deltaY } } var somePoint = Point(x: 1.0, y: 1.0) somePoint.moveByX(2.0, y: 3.0) print("(\(somePoint.x), \(somePoint.y))") // (3.0, 4.0) let fixedPoint = Point(x: 3.0, y: 3.0) // 结构体是值对象,使用let常量后,无法修改内部值 fixedPoint.moveByX(2.0, y: 3.0) // 抛错 ~~~ 由于结构体和枚举是值类型,默认情况下,实例方法中是不可以修改值类型的属性。我们只可以在外部修改值类型中的属性。如果你想在实例中修改值类型的属性,只需在方法名前加mutating关键字。 ### 在实例方法中修改结构体或枚举 在结构体和枚举内,self代表当前结构体或枚举,我们还可以通过mutating修改当前结构体或枚举。 ~~~ // 结构体测试 struct Point { var x = 0.0, y = 0.0 mutating func moveByX(deltaX: Double, y deltaY: Double) { self = Point(x: x + deltaX, y: y + deltaY) } } // 枚举测试 enum TriStateSwitch { case Off, Low, High mutating func next() { switch self { case Off: self = Low case Low: self = High case High: self = Off } } } var ovenLight = TriStateSwitch.Low print("\(ovenLight.next())") // High print("\(ovenLight.next())") // Off ~~~ 如上所示,在实例方法中就可修改self。 ## 类型方法 在方法中我们可以不初始化类就调用类的内部方法,这就是类型方法。 ~~~ class SomeClass { class func someTypeMethod() { // type method implementation goes here } } SomeClass.someTypeMethod() ~~~ 如上,我们只需在方法名前添加class关键字,就可直接使用类名加方法名调用someTypeMethod方法。 接下来为大家演示怎么直接使用结构体名加方法名。使用到的关键字为static ~~~ // 结构体 struct LevelTracker { // static修改属性,方法体要修改static属性,方法前要使用static static var highestUnlockedLevel = 1 static func levelIsUnlocked(level: Int) -> Bool { return level <= highestUnlockedLevel } } print("\(LevelTracker.levelIsUnlocked(2))") // false ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-30 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的中的Methods总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift属性(Properties)

最后更新于:2022-04-01 14:34:14

属性关联特定的类、结构和枚举的值。存储的属性存储常量和变量的值作为实例的一部分。 下面以User类为大家介绍属性,先上全部类代码。 ~~~ class DataImporter { var fileName = "data.txt" init() { print("初始化") } } private class Users { // 有默认值的属性 var id = 0 // 只指定类型的属性,有可能存储nil var name:String? // 懒加载,只是使用的时候才会初始化 lazy var importer = DataImporter() // get、set var qqTemp:String = "" var qq:String { // 如果使用get和set则不能使用当前属性名赋值,以免死循环 // 获得 get { print("获得") return self.qqTemp } // 设置 set { print("传入值:\(newValue)") self.qqTemp = newValue } } // 只读,read-only var height: Int { return 180 } // 属性观察 var address:String = "" { willSet { print("新地址:\(newValue)") } didSet { print("旧地址:\(oldValue)") } } // 静态变量 static var storedTypeProperty = "Some value." } ~~~ ## 基础属性 在类和结构体中可以建立存储属性,可以是常量、变量或枚举。也可以为属性设置初始值或不设置初始值。 ~~~ private class Users { // 有默认值的属性 var id = 0 // 只指定类型的属性,有可能存储nil var name:String? } ~~~ 使用: ~~~ let user = Users() // 有初始值 print("\(user.id)") // 0 // 无初始值 print("\(user.name)") // nil ~~~ ## 懒加载属性 你可以给属性添加初始值,但是有的时候你添加的初始值是一个很大的对象,但你只希望使用它的时候才初始化。这就需要用到懒加载,懒加载属性就是当我们首次使用的时候才会创建并分配存储空间。懒加载的关键字是lazy。 ~~~ // 懒加载,只是使用的时候才会初始化 lazy var importer = DataImporter() class DataImporter { var fileName = "data.txt" init() { print("初始化") } } print("\(user.importer.fileName)") // 先输出"初始化",后输出"data.txt" ~~~ ## Getter和Setter 我们都知道点方法的实质是调用get和set方法,在swift中也有get和set方法。 ~~~ var qqTemp:String = "" var qq:String { // 如果使用get和set则不能使用当前属性名赋值,以免死循环 // 获得 get { print("获得") return self.qqTemp } // 设置 set { print("传入值:\(newValue)") self.qqTemp = newValue } } user.qq = "937447974" // 传入值:937447974 print("\(user.qq)") // 获得 937447974 ~~~ 如上所示,在属性中我们可以更加精确的控制get和set方法。在set中,newValue就是外部传入的值。 ## 只读属性 在swift中也支持属性只读,简单点就是只写get方法,不写set方法。但是swift考虑这种情况有更好的方式实现。 ~~~ // 只读,read-only var height: Int { return 180 } ~~~ ## 属性观察 有的时候,你可能想监听设置属性的操作,同时又想使用当前属性名。因为get和set中是不能使用当前属性名的,鉴于这种情况,swift提供了两种操作符: - willSet:设置前调用,newValue代表外部传入值; - didSet:设置后调用,didSet代码原始值。 ~~~ // 属性观察 var address:String = "" { willSet { print("新地址:\(newValue)") } didSet { print("旧地址:\(oldValue)") } } //属性观察 willSet将设置,didSet已设置 user.address = "北京" // 新地址:北京 旧地址: user.address = "天安门" // 新地址:天安门 旧地址:北京 ~~~ ## 静态变量 在swift中也支持静态变量static,使用静态变量只需要使用类名。 ~~~ // 静态变量 static var storedTypeProperty = "Some value." // 静态变量 Users.storedTypeProperty = "static" print("\(Users.storedTypeProperty)") // static ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-29 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Classes and Structures总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift类和结构体(Classes and Structures)

最后更新于:2022-04-01 14:34:12

在Swift中,类和结构体是通用且灵活的架构,枚举是一组相关的值的集合,并允许你在代码中使用它们。你可以在类和结构体中添加属性和方法,如常量、变量和函数。 相对于其他编程语言,当你想创建类和结构体时,需要创建接口文件和实现文件。Swift不需要你创建接口文件和实现文件,如玩过OC的朋友都知道.h和.m文件。在Swift中,当你想创建类和结构体时,只需要创建一个文件。这个类和结构的外部接口可自动给其他代码使用。 ## 比较类和结构体 类和结构体有很多共同点: - 为存储值声明属性; - 定义函数的方法; - 定义下标并支持下标语法; - 为属性设置初始值; - 可扩展; - 为子类提供规范。 类支持的功能而结构体不支持的功能有: - 可继承,使一个类继承另一个类的特性; - 在运行过程中,可转换类型; - 在类销毁时释放资源; - 引用计数运行多个引用到同一个类实例。 ### 定义语法 类和结构体的定义语法是相似的,只是定义类用的关键字是class,定义结构体的关键字是struct。下面定义了一个空的类和结构体: ~~~ class SomeClass { } struct SomeStructure { } ~~~ 这里定义了有属性的类和结构体: ~~~ struct Resolution { var width = 0 var height = 0 } class VideoMode { var resolution = Resolution() var interlaced = false var frameRate = 0.0 var name: String? } ~~~ 可以看出,在类VideoMode中resolution指向一个结构体,name是一个可为nil或String类型的数据,其他属性都指定了默认值。 ### 类和结构体初始化 ~~~ let someResolution = Resolution() let someVideoMode = VideoMode() ~~~ ### 访问属性 访问类和结构体的属性可以使用点语法。 ~~~ print("someResolution.width:\(someResolution.width)") // 0 print("someVideoMode.resolution.width:\(someVideoMode.resolution.width)") // 0 ~~~ 你还可以通过点语法改变类或结构体中的数据。 ~~~ someVideoMode.resolution.width = 1280 print("someVideoMode.resolution.width:\(someVideoMode.resolution.width)") // 1024 ~~~ ### 通过成员初始化结构体 我们可以通过结构体的成员初始化结构体。这能帮我们快速初始化结构体,但是,初始化的过程必须全部成员一起初始化。 ~~~ let vga = Resolution(width: 640, height: 480) ~~~ ## 结构体和枚举是值类型 什么是值类型?值类型是一个值,当它分配给一个常量、变量或被作为参数传递时,它的值是被复制的。 ~~~ // 结构体测试 let hd = Resolution(width: 1920, height: 1080) var cinema = hd cinema.width = 2048 // 改变cinema的值,不会改变hd中的值 print("cinema.width:\(cinema.width)") // 2048 print("hd.width:\(hd.width)") // 1920 ~~~ 从上面的例子中可以看出,当你改变cinema的width值,不会引起hd中width的变化。 下面再用枚举举例: ~~~ // 枚举测试 enum CompassPoint { case North, South, East, West } var currentDirection = CompassPoint.West let rememberedDirection = currentDirection // 改变currentDirection,rememberedDirection的值不会变化 currentDirection = .East print("\(rememberedDirection)") // West ~~~ ## 类是引用对象 相当于值对象,引用对象在传递过程中是不会复制的,它们指向同一个实例。 ~~~ let tenEighty = VideoMode() tenEighty.frameRate = 25.0 // 引用tenEighty到alsoTenEighty,并改变alsoTenEighty中的值 let alsoTenEighty = tenEighty alsoTenEighty.frameRate = 30.0 print("tenEighty.frameRate:\(tenEighty.frameRate)") // 30 ~~~ 在这里修改alsoTenEighty的frameRate会发现tenEighty的frameRate值也发生变化。 ## 恒等算子 因为类是引用类型,它可能被多个常量或变量指向。我们需要一种机制判断这些常量或变量是否指向同一个实例。这里就需要用到恒等算子。 在swift中恒等算子有两种操作符: - ===:相同实例 - !==:不同实例 使用这些操作符来比较两个常量或变量是否引用同一个实例: ~~~ // 恒等算子是===或!== let tenEighty = VideoMode() let alsoTenEighty = tenEighty if tenEighty === alsoTenEighty { print("相同实例") } ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-28 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Classes and Structures总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift枚举(Enumerations)

最后更新于:2022-04-01 14:34:09

枚举是一组相关的值的集合,并允许你在代码中使用。 枚举允许所有的值是相同类型的,也可以是不同类型的,还可以为这些值设置默认值。 ## 枚举语法 枚举使用enum做为关键字,后跟枚举名,其内部值在一个{}内。 空枚举: ~~~ enum SomeEnumeration { // enumeration definition goes here } ~~~ 下面这个枚举包含四个值: ~~~ // 枚举基本类型 enum CompassPoint { case North case South case East case West } ~~~ 如果你觉得case的行数太多,枚举还支持简写,将其写在一行。 ~~~ enum Planet { case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } ~~~ 枚举是一个崭新的类型,你可以通过let或var去获得它们。 ~~~ // 使用 var directionToHead = CompassPoint.West ~~~ 如果你觉得写枚举名太麻烦,枚举还支持不写枚举名,直接使用点语法。 ~~~ directionToHead = .East ~~~ ## 枚举匹配 你可以使用switch或if做枚举匹配。 ~~~ var directionToHead = CompassPoint.South // if匹配 if directionToHead == CompassPoint.South { directionToHead = .East } // switch匹配 switch directionToHead { case .North: print("Lots of planets have a north") case .South: print("Watch out for penguins") case .East: print("Where the sun rises") default: print("default") } ~~~ ## 关联值 枚举支持关联值,在case后可以使用一个结构体。 ~~~ enum Barcode { case UPCA(Int, Int, Int, Int) case QRCode(String) } ~~~ 初始化 ~~~ var productBarcode = Barcode.UPCA(8, 85909, 51226, 3) productBarcode = .QRCode("ABCDEFGHIJKLMNOP") ~~~ 使用switch做匹配,这里用到了let ~~~ switch productBarcode { case .UPCA(let numberSystem, let manufacturer, let product, let check): print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") case .QRCode(let productCode): print("QR code: \(productCode).") } ~~~ 如果你觉得let太麻烦了,枚举的关联值匹配也支持不写let。 ~~~ switch productBarcode { case let .UPCA(numberSystem, manufacturer, product, check): print("UPC-A: \(numberSystem), \(manufacturer), \(product), \(check).") case let .QRCode(productCode): print("QR code: \(productCode).") } ~~~ ## 原始值 枚举支持为每个属性设置原始值,下面设置Character类型的原始值。 ~~~ enum ASCIIControlCharacter: Character { case Tab = "\t" case LineFeed = "\n" case CarriageReturn = "\r" } ~~~ ### 隐式分配原始值 在枚举中,还支持隐式分配枚举值。 ~~~ enum Planet: Int { case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune } ~~~ 在上面的例子中,整个枚举Planet为Int类型,并设置了Mercury=1。swift会自动为接下来的属性赋值Venus=2、Earth=3。。。 还有一种情况,你不给任何属性赋初始值,只是指定了枚举的类型。Swift也可以自动判断内部属性的原始值。 ~~~ enum CompassPoint: String { case North, South, East, West } ~~~ 如上所示,枚举会使North=”North”、South=“South”、East = “East”和West=“West”。 关于有原始值的枚举使用如下: ~~~ print("\(Planet.Earth.rawValue)") // 3 print("\(CompassPoint.West.rawValue)") // "West" ~~~ ### 通过原始值初始化 如同初始化类,你也可以通过原始值获取枚举。 ~~~ let possiblePlanet = Planet(rawValue: 7) ~~~ 但是通过原始值获取枚举时,有的时候获取不到,会返回nil。 ~~~ let positionToFind = 9 // 当原始值不匹配时,返回为nil if let somePlanet = Planet(rawValue: positionToFind) { switch somePlanet { case .Earth: print("Mostly harmless") default: print("Not a safe place for humans") } } else { print("There isn't a planet at position \(positionToFind)") } ~~~ ## 循环枚举 在枚举中可以设置任何原始值,同样可以设置枚举,并且是当前枚举,需要用到的关键字是indirect。 ~~~ enum ArithmeticExpression { case Number(Int) indirect case Addition(ArithmeticExpression, ArithmeticExpression) indirect case Multiplication(ArithmeticExpression, ArithmeticExpression) } ~~~ 如果你觉得每个case都写indirect太麻烦,枚举还支持你将indirect写到enum前。 ~~~ indirect enum ArithmeticExpression { case Number(Int) case Addition(ArithmeticExpression, ArithmeticExpression) case Multiplication(ArithmeticExpression, ArithmeticExpression) } ~~~ 下面介绍了怎么运用上面的循环枚举计算(5 + 4) * 2 ~~~ // 函数使用 func evaluate(expression: ArithmeticExpression) -> Int { switch expression { case .Number(let value): return value case .Addition(let left, let right): return evaluate(left) + evaluate(right) case .Multiplication(let left, let right): return evaluate(left) * evaluate(right) } } // evaluate (5 + 4) * 2 let five = ArithmeticExpression.Number(5) let four = ArithmeticExpression.Number(4) let sum = ArithmeticExpression.Addition(five, four) let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2)) print(evaluate(product)) ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-28 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Enumerations总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift闭包(Closures)

最后更新于:2022-04-01 14:34:07

闭包是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。 在Swift中的闭包与C、OC中的blocks和其它编程语言(如Python)中的lambdas类似。 闭包可以捕获和存储上下文中定义的的任何常量和变量的引用。这就是所谓的变量和变量的自封闭, 因此命名为”闭包“(“Closures)”)。Swift还会处理所有捕获的引用的内存管理。 全局函数和嵌套函数其实就是特殊的闭包。 闭包的形式有三种: - 全局函数都是闭包,有名字但不能捕获任何值。 - 嵌套函数都是闭包,且有名字,也能捕获封闭函数内的值。 - 闭包表达式都是无名闭包,使用轻量级语法,可以根据上下文环境捕获值。 Swift中的闭包有很多优化的地方: - 根据上下文推断参数和返回值类型; - 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return); - 可以使用简化参数名,如0,1(从0开始,表示第i个参数…); - 提供了尾随闭包语法(Trailing closure syntax)。 ## 闭包表达式 嵌套函数是非常强大的功能,在一个函数体内嵌套另一个函数。将函数作为参数和返回值也非常有用。这些都是一些特殊情况下的闭包。 闭包表达式是一种简短的、集中的语法。闭包表达式为了缩短代码以及优化代码的阅读性,提供了几种语法优化。这里使用数组的排序为大家展示闭包的优化。 ### Sort方法 ~~~ // 函数做参数,排序 let names = ["阳君", "937447974", "a", "b", "c"] func backwards(s1: String, _ s2: String) -> Bool { return s1 > s2 } var reversed = names.sort(backwards) ~~~ 从代码中可以看出,将函数作为参数传递对于代码的阅读性不是很好,这里就需要闭包表达式对其优化。 ### 闭包语法 闭包表达式的结构图如下所示: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-01_574e9af8cd710.jpg "") - parameters:闭包接受的参数; - return type:闭包运行完毕的返回值; - statements:闭包内的运行代码。 下面运用闭包表达式代替backwards函数对sort进行优化。 ~~~ reversed = names.sort({ (s1: String, s2: String) -> Bool in return s1 > s2 }) ~~~ 当要运行的代码很少时,你也可以将它们写在一行。 ~~~ reversed = names.sort( { (s1: String, s2: String) -> Bool in return s1 > s2 } ) ~~~ ### 通过上下文判断类型 在闭包中,我们不必写参数的类型和返回值的类型,闭包可以通过上下文自动判断参数类型和返回值类型。 ~~~ reversed = names.sort( { s1, s2 in return s1 > s2 } ) ~~~ ### 从单一表达式隐藏Return 在闭包中,如果运行的内容很少只有一行,则不必写return,闭包会自动返回。 ~~~ reversed = names.sort( { s1, s2 in s1 > s2 } ) ~~~ ### 速记参数名称 在闭包中,我们不必命名参数名称。闭包中的参数可使用`$`去获得,第一个参数为`$0`,第二个为`$1`。 ~~~ reversed = names.sort( { $0 > $1 } ) ~~~ ### 算子函数 当在闭包中,只有一个表达式,做操作。如在sort中,只有两个参数做比较操作。闭包支持只输入>或<做比较。 ~~~ reversed = names.sort(>) ~~~ ## 尾随闭包 如果函数需要一个闭包作为参数,且这个参数是最后一个参数。我们又不想在()内写太多代码,我们可以运用尾随闭包。尾随闭包意味着闭包可以放在函数参数列表外,也就是括号外。 ~~~ var reversed = names.sort() { $0 > $1 } ~~~ 当尾随闭包中的参数只有一个时,我们可以省略()。 ~~~ reversed = names.sort { $0 > $1 } ~~~ ## 捕获值 闭包可以根据上下文环境捕获到定义的常量和变量。闭包可以引用和修改这些捕获到的常量和变量,就算在原来的范围内定义为常量或者变量已经不再存在。在Swift中闭包的最简单形式是嵌套函数。 ~~~ func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 0 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer } let incrementByTen = makeIncrementer(forIncrement: 10) print("\(incrementByTen())") // prints "10" print("\(incrementByTen())") // prints "20" print("\(incrementByTen())") // prints "30" ~~~ 上面的例子介绍了,有个函数makeIncrementer,在函数内有一个嵌套函数incremented。嵌套函数可以通过上下文使用它的外部值runningTotal和amount。 当你想声明另一个闭包类型时,可以像声明属性一样声明。 ~~~ let incrementBySeven = makeIncrementer(forIncrement: 7) print("\(incrementBySeven())") // prints "7" print("\(incrementByTen())") // prints "40" ~~~ 可以看出两个闭包的引用是完全独立工作的。 ## 闭包是引用类型 运用属性执行的闭包后,我们可以用另一个属性去引用,对于两个属性来说,它们是完全相同的,指向同一个闭包。 ~~~ let alsoIncrementByTen = incrementByTen alsoIncrementByTen() // prints "50" ~~~ ## Noescape关键字 @noescape主要用于解决“保留环”问题如下所示,当你调用someFunctionWithEscapingClosure函数时,使用全局属性,会使用了self,这样你会发现每次调用闭包时,都会使用捕获self,这样容易造成内存泄露的问题,而且闭包中的操作其实是一成不变的,没有必要每次都访问。 Swift鉴于这种情况,希望在闭包内不使用self,因此产生了@noescape关键字。将@noescape写入闭包名前。这样在编写闭包内代码时,无须使用self属性,也避免了保留环问题。 ~~~ var completionHandlers: [() -> Void] = [] func someFunctionWithNoescapeClosure(@noescape closure: () -> Void) { closure() // completionHandlers.append(closure) //会报错,closure无法被保存 } func someFunctionWithEscapingClosure(completionHandler: () -> Void) { completionHandler() completionHandlers.append(completionHandler) } class SomeClass { var x = 10 func doSomething() { // 内存溢出,保留环问题 someFunctionWithEscapingClosure { self.x = 100 } someFunctionWithNoescapeClosure { x = 200 } } } let instance = SomeClass() instance.doSomething() print(instance.x) // prints "200" completionHandlers.first?() print(instance.x) // prints "100" ~~~ ## Autoclosures关键字 在闭包中,我们调用函数时,是将代码封装为一个闭包传递给函数。如下所示: ~~~ var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] func serveCustomer(customerProvider: () -> String) { print("Now serving \(customerProvider())!") } serveCustomer( { customersInLine.removeAtIndex(0) } ) // prints "Now serving Alex!" ~~~ 此时我们会思考,能否让代码直接为参数传递过去,也就是不用{}包含代码。@autoclosure关键字可以帮助你完成这种机制。 ~~~ // customersInLine is ["Ewa", "Barry", "Daniella"] func serveCustomer2(@autoclosure customerProvider: () -> String) { print("Now serving \(customerProvider())!") } // 闭包作为参数 serveCustomer2(customersInLine.removeAtIndex(0)) // prints "Now serving Ewa!" ~~~ 使用了@autoclosure默认也是使用@noescape,如果你只想使用autoclosure的特性,不想使用noescape的特性,你可以使用escape关键字,如下所示: ~~~ // customersInLine is ["Barry", "Daniella"] var customerProviders: [() -> String] = [] //autoclosure和escaping一起用 func collectCustomerProviders(@autoclosure(escaping) customerProvider: () -> String) { customerProviders.append(customerProvider) } // 添加闭包,并且闭包此时为参数 collectCustomerProviders(customersInLine.removeAtIndex(0)) collectCustomerProviders(customersInLine.removeAtIndex(0)) //循环使用闭包 for customerProvider in customerProviders { print("Now serving \(customerProvider())!") } // prints "Now serving Barry!" // prints "Now serving Daniella!" ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-28 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)中的Closures总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

Swift函数

最后更新于:2022-04-01 14:34:05

函数是一组特定的代码块,执行特定的任务。当你需要执行一个函数体时,你只需要调用函数的标示名。 在Swift中,函数是足够强大和灵活的。你可以传递参数,并且在函数执行完毕时也可以返回数据。参数可以提供默认值,以简化函数的调用。 每一个函数都是一个类型,这意味着可以你可以将函数作为参数传递到其他函数内,并返回一个函数。也可以将其他函数封装到一个函数内形成嵌套函数。 ## 定义和调用函数 下面代码定义了一个sayHello的函数,这个函数接受一个String类型的参数,并返回一个String类型的数据。当你想调用这个函数时,只需调用函数名,并传递想要传递的数据。 ~~~ func sayHello(personName: String) -> String { let greeting = "Hello, " + personName + "!" return greeting } print(sayHello("阳君")) // prints "Hello, 阳君!" ~~~ ## 函数的参数和返回值 在Swift中,函数的参数和返回值是非常灵活的,你可以定义任何类型的参数和返回值。 ### 函数不携带参数 ~~~ // 无参数,只有一个String类型的返回值 func sayHelloWorld() -> String { return "hello, world" } print(sayHelloWorld()) // prints "hello, world" ~~~ ### 函数携带多个参数 ~~~ // 传入两个参数,并返回一个String类型的数据 func sayHello(personName: String, alreadyGreeted: Bool) -> String { if alreadyGreeted { return "Hello again, \(personName)!" } else { return "Hello, \(personName)!" } } print(sayHello("阳君", alreadyGreeted: true)) // prints "Hello again, 阳君!" ~~~ ### 函数没有返回值 ~~~ // 传入一个String类型的数据,不返回任何数据 func sayGoodbye(personName: String) { print("Goodbye, \(personName)!") } sayGoodbye("阳君") // prints "Goodbye, 阳君!" ~~~ ### 函数有返回值 ### 函数有一个返回值 ~~~ // 返回一个Int类型的数据 func printAndCount(stringToPrint: String) -> Int { print(stringToPrint) return stringToPrint.characters.count } printAndCount("hello, world") // prints "hello, world" ~~~ ### 函数有多个返回值 ~~~ // 返回元组数据 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([8, -6, 2, 109, 3, 71]) print("min is \(bounds.min) and max is \(bounds.max)") // prints "min is -6 and max is 109" ~~~ ### 返回类型可选 当函数执行完毕时,会返回一定的数据。在有的情况下函数会返回nil的数据,此时只需要在声明的函数返回类型后添加“?”。 ~~~ // 返回一个元组或Nil 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([8, -6, 2, 109, 3, 71]) { print("min is \(bounds.min) and max is \(bounds.max)") // prints "min is -6 and max is 109" } ~~~ ## 参数名 ### 指定外部参数名 在外部调用函数时,我们希望在传递参数的数据前有提示,这样提升了代码的阅读性。如`sayHello("Bill", "Ted")` 增加外部参数名后变为`sayHello(to: "Bill", and: "Ted")`。 ~~~ // 指定外部参数名to和and func sayHello(to person: String, and anotherPerson: String) -> String { return "Hello \(person) and \(anotherPerson)!" } print(sayHello(to: "Bill", and: "Ted")) // prints "Hello Bill and Ted!" ~~~ ### 省略外部参数名 在一些特有的情况下,我们并不需要外部参数名。由于swift的函数机制,除第一个外的其他参数必须携带外部参数名。如果你不想使用外部参数名,在声明的时候可使用`_` 代替参数名。 ~~~ // 使用_省略外面参数名, func someFunction(firstParameterName: Int, _ secondParameterName: Int) { } someFunction(1, 2) ~~~ ### 参数默认值 在函数内部,我们可以为参数设置默认值。如果外部不传递数据时,我们就使用设置的默认值。 ~~~ // 设置默认值,当用户不传入时,使用默认值 func someFunction(parameterWithDefault: Int = 12) { print("\(parameterWithDefault)") } someFunction(6) // 6 someFunction() // 12 ~~~ ### 可变参数 有的时候,我们只知道传入函数的数据类型,不知道传入函数的数据个数,这个时候我们可以使用`...`代表允许传入未知个数的数据。 ~~~ // 传入的参数类型已知Double,个数未知 func arithmeticMean(numbers: Double...) -> Double { var total: Double = 0 for number in numbers { total += number } return total / Double(numbers.count) } print("\(arithmeticMean(1, 2, 3, 4, 5))") // 3.0 print("\(arithmeticMean(3, 8.25, 18.75))") // 10.0 ~~~ ### 常量和变量参数 在函数中,参数默认是let不可变。如果我们想让这个数据可变,可能需要在函数内用一个var转化。Swift鉴于这种情况,对参数做了优化,我们只需要用var声明这个参数,在函数内就可改变它的数据。 ~~~ // 默认为let常量参数,也可声明var可变参数,在函数内直接修改 func alignRight(var string: String, totalLength: Int, pad: Character) -> String { let amountToPad = totalLength - string.characters.count if amountToPad < 1 { return string } let padString = String(pad) for _ in 1...amountToPad { string = padString + string } return string } let originalString = "hello" let paddedString = alignRight(originalString, totalLength: 10, pad: "-") print("originalString:\(originalString); paddedString:\(paddedString);") // originalString:hello; paddedString:-----hello; ~~~ ### In-Out参数 当我们使用var让参数可变的时候,往往会思考我们改变这个参数的数据,是否会改变外部的数据呢?当你改变var声明的数据时,外面数据是不会发生任何改变的。但是有的情况,我们又希望外部数据也会跟着变化,此时我们只需要使用inout声明这个参数。 ~~~ // 使用inout声明的参数,在函数内修改参数值时,外面参数值也会变 func swapTwoInts(inout a: Int, inout _ b: Int) { let temporaryA = a a = b b = temporaryA } var someInt = 3 var anotherInt = 107 swapTwoInts(&someInt, &anotherInt) print("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // prints "someInt is now 107, and anotherInt is now 3" ~~~ ## 函数类型 所有的函数都是一个类型,只是这个类型有点特殊,它携带参数和返回值。也就意味着,你可以将一个函数用一个let或var声明的属性代替。当我们调用这个函数时,只需要调用这个声明的属性。 ### 使用函数类型 ~~~ // 加法 func addTwoInts(a: Int, _ b: Int) -> Int { return a + b } // 乘法 func multiplyTwoInts(a: Int, _ b: Int) -> Int { return a * b } // 函数体赋值为参数 var mathFunction: (Int, Int) -> Int = addTwoInts print("Result: \(mathFunction(2, 3))") // prints "Result: 5" // 函数体指向替换 mathFunction = multiplyTwoInts print("Result: \(mathFunction(2, 3))") // prints "Result: 6" // 函数体传递 let anotherMathFunction = addTwoInts // // anotherMathFunction is inferred to be of type (Int, Int) -> Int print("\(anotherMathFunction)") ~~~ ### 函数做参数 下面的例子介绍了怎么将一个函数作为一个参数传递到另一个函数内 ~~~ // 加法 func addTwoInts(a: Int, _ b: Int) -> Int { return a + b } // 其中一个参数为一个函数体 func printMathResult(mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) { print("Result: \(mathFunction(a, b))") } printMathResult(addTwoInts, 3, 5) // prints "Result: 8" ~~~ ### 函数做返回值 函数作为函数的参数传递,也就意味着函数可以作为函数的返回值。 ~~~ // 加1 func stepForward(input: Int) -> Int { return input + 1 } // 减1 func stepBackward(input: Int) -> Int { return input - 1 } // 使用函数体做返回类型 func chooseStepFunction(backwards: Bool) -> (Int) -> Int { return backwards ? stepBackward : stepForward } var currentValue = 3 // 此时moveNearerToZero指向stepForward函数 let moveNearerToZero = chooseStepFunction(currentValue > 0) // 调用函数体 currentValue = moveNearerToZero(currentValue) print("\(currentValue)... ") // prints “2...” ~~~ ## 嵌套函数 本章介绍的函数体都是全局函数的例子,它被定义在全局作用域中。您也可以在一个函数体内再定义一个函数,称为嵌套函数。 嵌套函数默认情况下是隐藏的,但你仍然可以调用和使用嵌套函数外的数据。 ~~~ // 函数体内部嵌套函数,并做返回类型 func chooseStepFunction(backwards: Bool) -> (Int) -> Int { // 嵌套函数1 func stepForward(input: Int) -> Int { return input + 1 } // 嵌套函数2 func stepBackward(input: Int) -> Int { return input - 1 } return backwards ? stepBackward : stepForward } var currentValue = -2 let moveNearerToZero = chooseStepFunction(currentValue > 0) currentValue = moveNearerToZero(currentValue) print("\(currentValue)... ") // prints "-1..." ~~~ ## 其他 ### 参考资料 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html) ### 文档修改记录 | 时间 | 描述 | |-----|-----| | 2015-10-27 | 根据 [The Swift Programming Language (Swift 2.1)](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)总结 | 版权所有:[http://blog.csdn.net/y550918116j](http://blog.csdn.net/y550918116j)
';

前言

最后更新于:2022-04-01 14:34:03

> 原文出处:[Swift学习](http://blog.csdn.net/column/details/swift937447974.html) 作者:[y550918116j](http://blog.csdn.net/y550918116j) **本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!** # Swift学习 > 根据苹果官方文档以及工作中的一些总结
';