Swift 语法概览(二)
7. 枚举
枚举是一组相同数据类型集合,Swift 中,枚举可以添加计算属性,实例方法,初始化方法等,类似 Class 行为。
enum CompassPoint {
case north, south
case east, west
}
var directionToHead = CompassPoint.west
// 类型推导
directionToHead = .east
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .east:
print("Where the sun rises")
default:
print("Not a safe place for humans")
}
Swift 中没有默认整数值,上面4个不是默认(0,1,2,3)
枚举迭代器
enum Beverage: CaseIterable {
case coffee, tea, juice
}
for beverage in Beverage.allCases {
print(beverage)
}
// coffee tea juice
关联值
关联值可以是任意类型,并且每个 Case 可以是不同类型的关联值
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
原始值
通过继承,枚举 Case 可以有默认值(原始值),所有 Case 都是相同类型,可以是字符串,字符,整数,浮点数,每个原始值都是不想等的。
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
隐式原始值
Int 和 String 类型的原始值有隐式模式值
// Int 默认为 0,后面 + 1
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
// String 默认值和 Case 名相等
enum CompassPoint: String {
case north, south, east, west
}
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
// 初始化,返回可选类型
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
递归枚举
枚举 Case 的关联值是当前枚举的实例,嵌套枚举值在 case 前面加 indirect 关键字。
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
// 也可以这样写
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
估值函数
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// Prints "18"
8. 结构体和类
Swift 中类和结构体有很很多相似的功能,他们之间的主要区别是:
- 类可以继承
- 类实例类型推导
- 析构方法
- 类是引用类型,结构体是值类型
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
let someResolution = Resolution()
let someVideoMode = VideoMode()
print("The width of someResolution is \(someResolution.width)")
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
someVideoMode.resolution.width = 1280
结构体类型有个自动生成的成员初始化方法,类没有
let vga = Resolution(width: 640, height: 480)
属性
属性分为存储属性和计算属性,存储属性存储变量,常量,计算属性计算值。
class DataImporter {
var filename = "data.txt"
}
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
}
let manager = DataManager();
// lazy 变量只有在使用的时候才会去初始化
// manager.import.fileName
计算属性
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
// 单条表达式隐藏 return
Point(x: origin.x + (size.width / 2),
y: origin.y + (size.height / 2))
}
set(newCenter) { // 不提供 newCenter 参数则会提供一个默认参数 newValue
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
// 只读计算属性,没有set,省略 get
var volum: Double {
return width * height * depth
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// Prints "square.origin is now at (10.0, 10.0)"
属性观察者
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
属性封装
定义封装属性
@propertyWrapper
struct TwelveOrLess {
private var number: Int
init() { self.number = 0 }
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
使用封装属性
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
// 等价于
struct SmallRectangle {
private var _height = TwelveOrLess()
private var _width = TwelveOrLess()
var height: Int {
get { return _height.wrappedValue }
set { _height.wrappedValue = newValue }
}
var width: Int {
get { return _width.wrappedValue }
set { _width.wrappedValue = newValue }
}
}
封装属性初始化
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
struct ZeroRectangle {
@SmallNumber var height: Int
@SmallNumber var height: Int = 1
@SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
@SmallNumber(maximum: 9) var width: Int = 2
}
ProjectedValue
封装属性提供的额外一个功能。
@propertyWrapper
struct SmallNumber {
private var number: Int
var projectedValue: Bool
init() {
self.number = 0
self.projectedValue = false
}
var wrappedValue: Int {
get { return number }
set {
if newValue > 12 {
number = 12
projectedValue = true
} else {
number = newValue
projectedValue = false
}
}
}
}
struct SomeStructure {
@SmallNumber var someNumber: Int
}
var someStructure = SomeStructure()
someStructure.someNumber = 4
print(someStructure.$someNumber)
// Prints "false"
someStructure.someNumber = 55
print(someStructure.$someNumber)
// Prints "true"
类型属性
类型公用的属性,类似 C 中的静态变量。
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
// 使用 class 允许子类覆盖
class var overrideableComputedTypeProperty: Int {
return 107
}
}
方法
方法分为实例方法和类型方法
实例方法
class Counter {
var count = 0
func increment() {
count += 1
// 等价于 self.count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
let counter = Counter()
counter.increment()
结构体,枚举这种值类型数据,默认函数不能修改属性,如要修改,需要加关键字:mutating
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
// 必须是 var 变量,不能 let
var somePoint = Point(x: 1.0, y: 1.0)
枚举
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
ovenLight.next()
// ovenLight is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off
类型方法
static 修饰的方法,类类型可以使用 class 修饰以允许子类覆盖该方法
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1
static func unlock(_ level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
// 取消不使用函数返回值时的编译器警告
@discardableResult
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
9. 下标语法
Swift 中类,结构体,枚举也可以使用下表语法
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
// 可以接收多个参数
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValid(row: row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValid(row: row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
// 静态
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)!
}
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Prints "six times three is 18"
TimesTable[10]
10. 继承
override 关键词修饰
class Train: Vehicle {
var a = 10
// final 修饰的无法被继承
final var b = 20
override func makeNoise() {
super.makeNoise()
print("Choo Choo")
}
}
11. 初始化(构造器)
不带参数初始化方法
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
自定义带参数初始化方法
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
指定初始化
指定初始化方法会初始化该类所有的存储属性以及完成父类初始化操作
便捷初始化
convenience init() {
}
原则
- 指定初始化方法必须调用直接父类的指定初始化方法
- 便捷初始化方法必须调用本类的其他初始化方法
- 便捷初始化方法必须最终调用本类的指定初始化方法
失败的初始化
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
// Prints "An animal was initialized with a species of Giraffe"
必要的初始化
使用 required 修饰表示该初始化方法子类必须去实现
class SomeClass {
required init() {
// initializer implementation goes here
}
}
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
使用闭包(函数)给属性设置默认值
class SomeClass {
let someProperty: SomeType = {
// create a default value for someProperty inside this closure
// someValue must be of the same type as SomeType
return someValue
}()
}
12. 析构函数
析构函数在对象释放之前调用,使用 deinit 关键字,只有对象才有析构
deinit {
// perform the deinitialization
}
13. 类型反射
is
判断对象是否是某个类或者其子类的实例
as
将对象转化成某个类的实例,主要是父类向下转子类(可能转失败)
当不确定是否能转成功时使用 as?,否则使用 as!
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
// Prints "Media library contains 2 movies and 3 songs"
for item in library {
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)")
}
}
// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley
Any && AnyObject
Any 可以代表任意类型
AnyObject 代表任意类对象类型
var things = [Any]()
things.append(0)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
print("some other double value that I don't want to print")
case let someString as String:
print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called \(movie.name), dir. \(movie.director)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
}
}
14. 扩展
类似于 OC 中的 Category,Swift 的 Extension 可以:
- 添加计算类型属性
- 定义实例方法和类方法
- 提供新的初始化方法
- 定义下标
- 定义新的嵌套类型
- 遵循新的协议
extension SomeType : SomeProtocol, AnotherProtocol{
}
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters"
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters"
15. 协议
定义方法,属性,有遵循该协议的类提供 具体的实现。
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
static func someTypeMethod()
func random() -> Double
mutating func toggle()
}
class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol {
weak var delegate: DiceGameDelegate?
}
16. 范型
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
func findIndex<T: Equatable>(of valueToFind: T, in array:[T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
// Associated Types in Action
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
17. 不透明类型
英文太烂,文档看得晕呼呼的,等什么时候静下心来慢慢看,大概意思感觉有点类似 OC 中的类蔟
func makeOpaqueContainer<T>(item: T) -> some Container {
return [item]
}
// 告诉你函数的返回值是某种 Container 类型