Swift 4.2 更新大全

1、Bool.toggle

struct Layer {
    var isHidden = false
}

struct View {
    var layer = Layer()
}

var view = View()

// Before:
view.layer.isHidden = !view.layer.isHidden
view.layer.isHidden

// Now:
view.layer.isHidden.toggle()


2、對Sequence and Collection 新增一些方法

  • 新增allSatisfy 會根據(jù)closure中的謂詞進(jìn)行判斷,只有全部為true才會返回true
let digits = 0...9
let areAllSmallerThanTen = digits.allSatisfy { $0 < 10 }
areAllSmallerThanTen // true

let areAllEven = digits.allSatisfy { $0 % 2 == 0 }
areAllEven    // false
  • 新增last(where:) 從后往前開始遍歷,直到找到滿足條件的第一個元素,返回改元素
let digits = 0...9
let lastEvenDigit = digits.last { $0 % 2 == 0 }
lastEvenDigit //8
  • 新增 lastIndex(where:) 從后往前開始遍歷,直到找到滿足條件的第一個元素,返回其Index
let text = "Vamos a la playa"
let lastWordBreak = text.lastIndex(where: { $0 == "l" })
let lastWord = lastWordBreak.map { text[text.index(after: $0)...] }
lastWord // aya
  • 新增lastIndex(of:)從后往前遍歷,直到輸入的值和數(shù)組中的值相等,返回其Index
text.lastIndex(of: "l") == lastWordBreak //true
  • 移除index(of:)index(where:),分別用firstIndex(of:)firstIndex(where:) 代替
let text = "Vamos a la playa"
let firstWordBreak = text.firstIndex(where: { $0 == " " })
let firstWord = firstWordBreak.map { text[..<$0] }
firstWord //Vamos
  • 新增removeAll(where:) 刪除滿足條件的元素
var numbers = Array(1...10)
numbers.removeAll(where: { $0 % 2 == 0 })
numbers // 1 3 5 7 9


3、新增協(xié)議 CaseIterable

協(xié)議大多用在enum中,添加了一個allCases屬性,該屬性是一個collection

3.1 簡單使用

enum Terrain: CaseIterable {
    case water
    case forest
    case desert
    case road
}

Terrain.allCases // [water,forest,desert,road]
Terrain.allCases.count // 4

3.2 這個屬性可以用在TableView Sections

enum TableSection: Int, CaseIterable {
    /// The section for the search field
    case search = 0
    /// Featured content
    case featured = 1
    /// Regular content cells
    case standard = 2
}

func numberOfSections(in tableView: UITableView) -> Int {
    return TableSection.allCases.count
}

override func tableView(_ tableView: UITableView,
    cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
    let section = TableSection.allCases[indexPath.section]
    switch section {
    case .search: ...
    case .featured: ...
    case .standard: ...
    }
}

3.3 還可以在一個Enum中的case引用另外一個Enum,但是必須重寫allCases

enum Intensity: CaseIterable {
    case light
    case medium
    case hard
}

enum Workout {
    case resting
    case running(Intensity)
    case cycling(Intensity)
}

extension Workout: CaseIterable {
    static var allCases: [Workout] {
        return [.resting]
            + Intensity.allCases.map(Workout.running)
            + Intensity.allCases.map(Workout.cycling)
    }
}

Workout.allCases
/*  .resting
    .running(.light)
    .running(.medium)
    .running(.hard)
    .cycling(.light)
    .cycling(.medium)
    .cycling(.hard) */
Workout.allCases.count // 7

3.4 為了防止在自定義的allCases中漏掉新增的case,可以在內(nèi)部新增一個方法,用于提示我們需要做一些改變

extension Workout: CaseIterable {
    static var allCases: [Workout] {
        /// Dummy function whose only purpose is to produce
        /// an error when a new case is added to Workout. Never call!
        @available(*, unavailable, message: "Only for exhaustiveness checking, don't call")
        func _assertExhaustiveness(of workout: Workout, never: Never) {
            switch workout {
            case .resting,
                 .running(.light), .running(.medium), .running(.hard),
                 .cycling(.light), .cycling(.medium), .cycling(.hard):
                break
            }
        }

        return [.resting]
            + Intensity.allCases.map(Workout.running)
            + Intensity.allCases.map(Workout.cycling)
    }
}

這樣子當(dāng)我們新增了case,就會提示我們需要進(jìn)行適配了。

這個動作就像我們在做單元測試一樣,所以當(dāng)新增了case,務(wù)必適配allCases

3.5 在其他的類型中使用 CaseInterable 協(xié)議,

除了普通的enum有著默認(rèn)的實現(xiàn),其他的都需要自己去重寫allCases

  • Bool
extension Bool: CaseIterable {
    public static var allCases: [Bool] {
        return [false, true]
    }
}

Bool.allCases // → [false, true]
  • UInt8
extension UInt8: CaseIterable {
    public static var allCases: ClosedRange<UInt8> {
        return .min ... .max
    }
}

UInt8.allCases.count // → 256

這里官方還給出了建議,就是如果生成的時候很消耗資源,請考慮使用lazy,例如:

UInt8.allCases.lazy.count

3.6 如果allCases中有optional,那么必須定義 public typealias AllCases = [?],否則編譯器會報錯

extension Optional: CaseIterable where Wrapped: CaseIterable {
    public typealias AllCases = [Wrapped?]
    public static var allCases: AllCases {
        return [nil] + Wrapped.allCases.map { $0 }
    }
}
enum CompassDirection: CaseIterable {
    case north, south, east, west
}

CompassDirection.allCases // → [north, south, east, west]
CompassDirection.allCases.count // → 4
CompassDirection?.allCases // → [nil, north, south, east, west]
CompassDirection?.allCases.count // → 5


4、統(tǒng)一隨機(jī)數(shù)的接口

4.1數(shù)字類型

Int.random(in: 1...1000)
UInt8.random(in: .min ... .max)
Double.random(in: 0..<1)

4.2 Bool

Bool.random()

4.3 collection(集合)

  • 隨機(jī)取一個元素
let emotions = "??????????????????"
let randomEmotion = emotions.randomElement()! 
  • 打亂集合

    • let

      let numbers = 1...10
      let shuffled = numbers.shuffled()
      
    • var

      var mutableNumbers = Array(numbers)
      // Shuffles in place
      mutableNumbers.shuffle()    
      

4.4 重寫隨機(jī)數(shù)生成器

/// A dummy random number generator that just mimics `SystemRandomNumberGenerator`.
struct MyRandomNumberGenerator: RandomNumberGenerator {
    var base = SystemRandomNumberGenerator()
    mutating func next() -> UInt64 {
        // 如果有特殊的需求,就這這里進(jìn)行處理
        return base.next()
    }
}

var customRNG = MyRandomNumberGenerator()
Int.random(in: 0...100, using: &customRNG)

4.5 為enum添加隨機(jī)函數(shù)

enum Suit: String, CaseIterable {
    case diamonds = "?"
    case clubs = "?"
    case hearts = "?"
    case spades = "?"

    static func random<T: RandomNumberGenerator>(using generator: inout T) -> Suit {
        // Using CaseIterable for the implementation
        return allCases.randomElement(using: &generator)!

    }

    static func random() -> Suit {
        // 如果不需要系統(tǒng)的隨機(jī)數(shù)生成,就把這個類型替換
        var rng = SystemRandomNumberGenerator()
        return Suit.random(using: &rng)
    }
}

let randomSuit = Suit.random()
randomSuit.rawValue 


5、重新設(shè)計了Hashable

減少hash值碰撞

栗子:

struct Point {
    var x: Int { didSet { recomputeDistance() } }
    var y: Int { didSet { recomputeDistance() } }

    /// Cached. Should be ignored by Equatable and Hashable.
    private(set) var distanceFromOrigin: Double

    init(x: Int, y: Int) {
        self.x = x
        self.y = y
        self.distanceFromOrigin = Point.distanceFromOrigin(x: x, y: y)
    }

    private mutating func recomputeDistance() {
        distanceFromOrigin = Point.distanceFromOrigin(x: x, y: y)
    }

    private static func distanceFromOrigin(x: Int, y: Int) -> Double {
        return Double(x * x + y * y).squareRoot()
    }
}

extension Point: Equatable {
    static func ==(lhs: Point, rhs: Point) -> Bool {
        // Ignore distanceFromOrigin for determining equality
        return lhs.x == rhs.x && lhs.y == rhs.y
    }
}

extension Point: Hashable {
    func hash(into hasher: inout Hasher) {
        // Ignore distanceFromOrigin for hashing
        hasher.combine(x)
        hasher.combine(y)
    }
}

let p1 = Point(x: 3, y: 4)
p1.hashValue
let p2 = Point(x: 4, y: 3)
p2.hashValue
assert(p1.hashValue != p2.hashValue)

如果類/結(jié)構(gòu)體中的數(shù)據(jù)類型都是一種,比如Int,在采用return x ^ y這種算法,就很容易發(fā)生碰撞,所以我們在重寫hash的時候一定要注意這個東西,要在性能和碰撞之間做平衡。


6、動態(tài)映射的修改

func isEncodable(_ value: Any) -> Bool {
    return value is Encodable
}

// This would return false in Swift 4.1
let encodableArray = [1, 2, 3]
isEncodable(encodableArray)

// Verify that the dynamic check doesn't succeed when the conditional conformance criteria aren't met.
struct NonEncodable {}
let nonEncodableArray = [NonEncodable(), NonEncodable()]
isEncodable(nonEncodableArray)//false
assert(isEncodable(nonEncodableArray) == false)


7、擴(kuò)展中的合成一致性

enum Either<Left, Right> {
    case left(Left)
    case right(Right)
}

// No code necessary
extension Either: Equatable where Left: Equatable, Right: Equatable {}
extension Either: Hashable where Left: Hashable, Right: Hashable {}

Either<Int, String>.left(42) == Either<Int, String>.left(42) //true


8、Range的修改

官方說道:CountableRangeCountableClosedRange還存在,但是不應(yīng)該在之后的代碼中使用到他們了,這個是為了兼容之前的代碼

let integerRange: Range = 0..<5
// integer是一個collection,所以可以調(diào)用map方法
let integerStrings = integerRange.map { String($0) }
integerStrings

let floatRange: Range = 0.0..<5.0
// 以為float不是一個collection,所以不能調(diào)用map方法
//floatRange.map { String($0) } // error!


9、新增@dynamicMemberLookup

允許我們運(yùn)行時,動態(tài)查找屬性可以用來修飾class/struct/protocol/enum

使用@dynamicMemberLookup需要重寫如下代碼:

subscript(dynamicMember input: String) -> XX {
    get {
            guard let value = getenv(name) else { return nil }
            return xx
        }
    nonmutating set {
        if let value = newValue {
            setenv(name, value, /*overwrite:*/ 1)
        } else {
            unsetenv(name)
        }
    }
}
@dynamicMemberLookup
struct Uppercaser {
    subscript(dynamicMember input: String) -> String {
        return input.uppercased()
    }
}

Uppercaser().hello // → "HELLO"
// You can type anything, as long as Swift accepts it as an identifier.
Uppercaser().k?seso?e // → "K?SESOSSE"


10、guard let self = self

喜大奔普終于不用寫

guard let `self` = self ...


11、if let self = self { … }

也不用這樣子寫啦

if let weakself = self { … }


12、支持#warning#error

  • error

    #if MYLIB_VERSION < 3 && os(macOS)
    #error("MyLib versions < 3 are not supported on macOS")
    #endif
    
  • warning

    func doSomethingImportant() {
        #warning("TODO: missing implementation")
    }
    doSomethingImportant()
    


13、#if compiler version directive

#if compiler(>=4.2)
print("Using the Swift 4.2 compiler or greater in any compatibility mode")
print("swift編譯器大于等于4.2") //菜雞翻譯,如果不準(zhǔn),煩請告知
#endif

#if swift(>=4.2)
print("Using the Swift 4.2 compiler or greater in Swift 4.2 or greater compatibility mode")
print("使用swift4.2編譯器編譯swift大于/等于4.2的版本") //菜雞翻譯,如果不準(zhǔn),煩請告知
#endif

#if compiler(>=5.0)
print("Using the Swift 5.0 compiler or greater in any compatibility mode")
print("swift編譯器大于等于5.0") //菜雞翻譯,如果不準(zhǔn),煩請告知
#endif


14、MemoryLayout中新增offset(of:)方法

struct Point {
    var x: Float
    var y: Float
    var z: Float
}

MemoryLayout<Point>.offset(of: \Point.x) // 0
MemoryLayout<Point>.offset(of: \Point.y) // 4
MemoryLayout<Point>.offset(of: \Point.z) // 8

官方解釋:

許多圖形和數(shù)學(xué)庫接受任意輸入格式的輸入數(shù)據(jù),用戶在設(shè)置輸入緩沖區(qū)時必須向API描述。例如,OpenGL允許您使用一系列glVertexAttribPointerAPI 調(diào)用來描述頂點緩沖區(qū)的布局。在C中,您可以使用標(biāo)準(zhǔn)offsetof宏來獲取結(jié)構(gòu)中字段的偏移量,允許您使用編譯器對類型布局的了解來填充這些函數(shù)調(diào)用:

//我們的一個頂點條目的布局
struct MyVertex {
   float position [ 4 ];
  float 正常 [ 4 ];
  uint16_t texcoord [ 2 ];
};

enum MyVertexAttribute {Position,Normal,TexCoord};

glVertexAttribPointer(Position,4,GL_FLOAT,GL_FALSE,
                       sizeof(MyVertex),(void *)offsetof(MyVertex,position));
glVertexAttribPointer(Normal,4,GL_FLOAT,GL_FALSE,
                       sizeof(MyVertex),(void *)offsetof(MyVertex,normal));
glVertexAttribPointer(TexCoord,2,GL_UNSIGNED_BYTE,GL_TRUE,
                       sizeof(MyVertex),(void *)offsetof(MyVertex,texcoord));

目前offsetof在Swift中沒有相同的功能,因此這些API的用戶必須在C中編寫代碼的這些部分,或者在頭腦中執(zhí)行Swift內(nèi)存布局,如果他們更改了數(shù)據(jù)布局或Swift,則容易出錯。編譯器實現(xiàn)更改其布局算法(它保留權(quán)利)。


15、新增兩個修飾符@inlinable@usableFromInline

  • @inlinable : 開發(fā)者可以將一些公共功能注釋為@inlinable。這給編譯器提供了優(yōu)化跨模塊邊界的泛型代碼的選項
@inlinable public func allEqual<T>(_ seq: T) -> Bool
    where T : Sequence, T.Element : Equatable {
  var iter = seq.makeIterator()
  guard let first = iter.next() else { return true }

  func rec(_ iter: inout T.Iterator) -> Bool {
    guard let next = iter.next() else { return true }
    return next == first && rec(&iter)
  }

  return rec(&iter)
}
  • usableFromInline: 使用@usableFromInline在你的“ABI-public”庫中進(jìn)行某些內(nèi)部聲明,允許它們在可鏈接函數(shù)中使用。

    public class C {
      @usableFromInline internal class D {
        @usableFromInline internal func f() {}
        
        @inlinable internal func g() {}
      }
    }
    


16、withUnsafePointer(to::) 和 withUnsafeBytes(of::)

withUnsafePointerwithUnsafeBytes通過指針提供對變量和屬性的內(nèi)存表示的臨時范圍訪問。它們當(dāng)前只接受inout參數(shù),這使得處理不可變值的內(nèi)存表示比它應(yīng)該做的更尷尬和更低效,需要復(fù)制:

let x = 1 + 6
var x2 = x
let value = withUnsafeBytes(of: &x2) {
    ptr in
    return (ptr[0] + ptr[1])
} 
value // 7

其內(nèi)部實現(xiàn)如下:

public func withUnsafePointer<T, Result>(
  to value: /*borrowed*/ T,
  _ body: (UnsafePointer<T>) throws -> Result
) rethrows -> Result

public func withUnsafeBytes<T, Result>(
  of value: /*borrowed*/ T,
  _ body: (UnsafeRawBufferPointer) throws -> Result
) re
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容