@propertyWrapper
可以先把它理解成一组特别的 getter 和 setter:它提供一个特殊的盒子,把原来值的类型包装进去。被 property wrapper 声明的属性,实际上在存储时的类型是 property wrapper 这个“盒子”的类型,只不过编译器施了一些魔法,让它对外暴露的类型依然是被包装的原来的类型。
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
typealias
是特定类型的别名。类型,例如Int
、Double
、UIViewController
/// 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
- 给闭包取别名
typealias SingleImageFetcherSuccess = (UIImage) -> Void
public func onSuccess(_ success: @escaping SingleImageFetcherSuccess) -> Self {
self.success = success
return self
}
- 支持泛型的别名, 类型别名允许我们编写更少的代码并简化代码中的定义
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>
- 元组的类型别名
/// 使用泛型和元组来定义类型,而不是必须用结构体
typealias Generation<T: Numeric> = (initial: T, seed: T, count: Int, current: T)
/// 如果定义这样的类型别名,则实际上可以像初始化一个结构体那样对其进行初始化:
let firstGeneration = Generation(initial: 10, seed: 42, count: 0, current: 10)
- 组合协议
protocol CanRead {}
protocol CanWrite {}
protocol CanAuthorize {}
protocol CanCreateUser {}
typealias Administrator = CanRead & CanWrite & CanAuthorize & CanCreateUser
typealias User = CanRead & CanWrite
typealias Consumer = CanRead
-
关联类型
结合前面的
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
- 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
- Infix Operators
infix operator --
func --(lhs: Int, rhs: Int) -> Int {
lhs - (rhs + rhs)
}
print(5 -- 1) // 3
print(10 -- 2) // 6
- 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