Swift关键字

1,099 阅读5分钟

@propertyWrapper

可以先把它理解成一组特别的 getter 和 setter:它提供一个特殊的盒子,把原来值的类型包装进去。被 property wrapper 声明的属性,实际上在存储时的类型是 property wrapper 这个“盒子”的类型,只不过编译器施了一些魔法,让它对外暴露的类型依然是被包装的原来的类型。

PropertyWrapper基础

PropertyWrapper进阶 PropertyWrapper有它使用的局限性

private enum Key: String {
    case userId
    case sessionId
}

/// @propertyWrapper 固定写法
@propertyWrapper
struct JNDefaults<T> {
    
    let key: String
    let defaultValue:T
    
    // wrappedValue 这个名字是固定写法
    var wrappedValue:T {
        set {
            UserDefaults.standard.setValue(newValue, forKey: key)
        }
        get {
            UserDefaults.standard.value(forKey: key) as? T ?? defaultValue
        }
    }
}

enum GloablSettings {
    
    ///固定写法 前调用自定义的类型
    @JNDefaults(key: Key.userId.rawValue, defaultValue: nil)
    static var userId:String?
    
    @JNDefaults(key: Key.sessionId.rawValue, defaultValue: nil)
    static var sessionId: String?
}

/// 只需要这样调用 就实现了存储到UserDefaults
GloablSettings.userId = "15"

我觉得这个东西代码看多就理解了,很多都是固定写法,正确应用到自己的实际开发中去,可读性和简洁性会提高很多

@propertyWrapper
struct Trimmed {
    private(set) var value: String = ""

    var wrappedValue: String {
        get { value }
        set { value = newValue.trimmingCharacters(in: .whitespacesAndNewlines) }
    }

    init(initialValue: String) {
        self.wrappedValue = initialValue
    }
}
struct Post {
    @Trimmed var title: String
    @Trimmed var body: String
}

let quine = Post(title: "  Swift Property Wrappers  ", body: "…")
quine.title // "Swift Property Wrappers" (no leading or trailing spaces!)

quine.title = "      @propertyWrapper     "
quine.title // "@propertyWrapper" (still no leading or trailing spaces!)

fallthrough

通常情况下, switch只会执行一个case, 在某些场景下,下一个case也需要执行,这时就可以用上fallthrough

使用前

enum AuthStatus {
    case authorized, suspicious, unauthorized
}
let authStatus: AuthStatus = .unauthorized
switch authStatus {
case .suspicious:
    signOut()
case .unauthorized:
    signIn()
    getLocation()
    getWeather()
case .authorized:
    getLocation()
    getWeather()
}

可以改成用 fallthrough, 加上fllthrough后,会接着执行下一个case ,省去case .unauthorized的一些重复代码

switch authStatus {
case .suspicious:
    signOut()
case .unauthorized:
    signIn()
    fallthrough
case .authorized:
    getLocation()
    getWeather()
}

inout

参数是一个常量,不可更改它的值,但在某些场景,需要变更参数,这时就可以用上inout

struct Article {
    var readers: [String]
}
var article = Article(readers: [])
func add(reader: String, to article: inout Article) {
    article.readers.append(reader)
    refreshUI()
    storeToCoreData()
}
add(reader: "Dylan", to: &article)
add(reader: "Mike", to: &article)
add(reader: "Jenny", to: &article)
print(article.readers) /// ["Dylan", "Mike", "Jenny"]

indirect

indirect 允许 enum 枚举类型引用它自身,比如下面的代码,如果不加上indirect,将会报错

indirect enum City {
    case boston
    case longIsland
    case newYork(nearBy: [City])
}
let boston: City = .boston
let longIsland: City = .longIsland
let newYork: City = .newYork(nearBy: [.boston, .longIsland])

associatedtype

associatedtype允许我们在协议中使用泛型

/// associatedtype CombinableData -> CombinableData 作为泛型
/// 使用时 CombinableData 替换成同一种类型 比如 Double, 比如String
protocol Combinable {
    associatedtype CombinableData
    var dataOne: CombinableData { get set }
    var dataTwo: CombinableData { get set }
    func getCombination() -> CombinableData
}
struct NumberCombination: Combinable {
    var dataOne: Double
    var dataTwo: Double
    func getCombination() -> Double {
        dataOne + dataTwo
    }
}
struct StringCombination: Combinable {
    var dataOne: String
    var dataTwo: String
    func getCombination() -> String {
        dataOne + dataTwo
    }
}

subscript

subscript 允许我们自定义下标方法,也就是subscript定义个方法,然后用[]语法去访问

extension Array {
    /// subscript 定义
    subscript(from index: Int) -> Element? {
        if index >= 0, index < count {
            return self[index]
        } else {
            return nil
        }
    }
}
let names: [String] = ["Dylan"]
/// [] 访问
print(names[from: 2]) // nil

typealias

  1. typealias 是特定类型的别名。类型,例如 IntDoubleUIViewController
/// 1 Int -> Money 可读性更强
struct Bank {
  typealias Money = Int
  private var credit: Money = 0
  mutating func deposit(amount: Money) {
    credit += amount
  }
  mutating func withdraw(amount: Money) {
    credit -= amount
  }
}
/// 2 取一个别名支持跨平台
#if os(macOS)
import AppKit
public typealias KFCrossPlatformImage = NSImage
public typealias KFCrossPlatformView = NSView
public typealias KFCrossPlatformColor = NSColor
public typealias KFCrossPlatformImageView = NSImageView
public typealias KFCrossPlatformButton = NSButton
#else
import UIKit
public typealias KFCrossPlatformImage = UIImage
public typealias KFCrossPlatformColor = UIColor
#if !os(watchOS)
public typealias KFCrossPlatformImageView = UIImageView
public typealias KFCrossPlatformView = UIView
public typealias KFCrossPlatformButton = UIButton
#if canImport(TVUIKit)
import TVUIKit
#endif
#else
import WatchKit
#endif
#endif
  1. 给闭包取别名
typealias SingleImageFetcherSuccess = (UIImage) -> Void

public func onSuccess(_ success: @escaping SingleImageFetcherSuccess) -> Self {
    self.success = success
    return self
}
  1. 支持泛型的别名, 类型别名允许我们编写更少的代码并简化代码中的定义
typealias Handler<In> = (In, HTTPResponse?, Context) -> Void

func handle(success: Handler<Int>?, 
            failure: Handler<Error>?,
           progress: Handler<Double>?){
}
struct ComputationResult<T> {
  private var result: T
}

typealias DataResult = ComputationResult<Data>
typealias StringResult = ComputationResult<String>
typealias IntResult = ComputationResult<Int>
  1. 元组的类型别名
/// 使用泛型和元组来定义类型,而不是必须用结构体
typealias Generation<T: Numeric> = (initial: T, seed: T, count: Int, current: T)
/// 如果定义这样的类型别名,则实际上可以像初始化一个结构体那样对其进行初始化:
let firstGeneration = Generation(initial: 10, seed: 42, count: 0, current: 10)
  1. 组合协议
protocol CanRead {}
protocol CanWrite {}
protocol CanAuthorize {}
protocol CanCreateUser {}

typealias Administrator = CanRead & CanWrite & CanAuthorize & CanCreateUser
typealias User = CanRead & CanWrite
typealias Consumer = CanRead
  1. 关联类型

    结合前面的associatedtype不难理解

public protocol TransformType {
	associatedtype Object
	associatedtype JSON

	func transformFromJSON(_ value: Any?) -> Object?
	func transformToJSON(_ value: Object?) -> JSON?
}

open class DateTransform: TransformType {
	public typealias Object = Date
	public typealias JSON = Double

	public init() {}

	open func transformFromJSON(_ value: Any?) -> Date? {
		if let timeInt = value as? Double {
			return Date(timeIntervalSince1970: TimeInterval(timeInt))
		}

		if let timeStr = value as? String {
			return Date(timeIntervalSince1970: TimeInterval(atof(timeStr)))
		}

		return nil
	}

	open func transformToJSON(_ value: Date?) -> Double? {
		if let date = value {
			return Double(date.timeIntervalSince1970)
		}
		return nil
	}
}

operator

  1. Prefix Operators ,前置操作符
//自定义操作符 ||
prefix operator ||
// || 后跟 rhs 值,这个值将会进行 -abs(rhs) 操作
prefix func ||(rhs: Int) -> Int {   
   -abs(rhs)
}
let numberOne = -3
let numberTwo = 3
print(||numberOne) // -3
print(||numberTwo) // -3
  1. Infix Operators
infix operator --
func --(lhs: Int, rhs: Int) -> Int {
    lhs - (rhs + rhs)
}
print(5 -- 1) // 3
print(10 -- 2) // 6
  1. Postfix Operators
postfix operator ++
postfix func ++(lhs: Int) -> Int {
    lhs + lhs
}
print(2++) // 4
print(10++) // 20

dynamic

指明编译器不会对类成员或者函数的方法进行内联或虚拟化。这意味着对这个成员的访问是使用Objective-C进行时进行动态派发的(代替静态调用)

class Person {
  //隐式指明含有"obj"属性
  //这对依赖于 Objc-C 黑魔法的库或者框架非常有用
  //比如 KVO KVC Swizzling
  dynamic var name:String?
}

unowned

让循环引用中的实例 A 不要强引用实例 B。前提条件是实例 B 的生命周期要长于 A 实例。

class Person  
{  
    var occupation:Job?  
}

//当 Person 实例不存在时,job 也不会存在。job 的生命周期取决于持有它的 Person。
class Job  
{  
    unowned let employee:Person

    init(with employee:Person)  
    {  
        self.employee = employee  
    }  
}

weak

允许循环引用中的实例 A 弱引用实例 B ,而不是强引用。实例 B 的生命周期更短,并会被先释放。

class Person  
{  
    var residence:House?  
}

class House  
{  
    weak var occupant:Person?  
}

var me:Person? = Person()  
var myHome:House? = House()

me!.residence = myHome  
myHome!.occupant = me

me = nil  
myHome!.occupant // myHome 等于 nil