Reading Notes - The most complete analysis of new features in Swift 4

Author: Liu Duo
This article is an original article, please indicate the author and source for reprinting

WWDC 2017 brought a lot of surprises. Swift 4 also came to us with the beta version of Xcode 9, and many powerful new features are worth looking forward to using it in official projects. Because Swift 4 is open source, if you follow swift-evolutionthe project, you should already be aware of its new features in advance . This article refers to WWDC 2017 and various materials, from the aspects of syntax, strings, standard library, construction process, etc., to introduce and analyze these new features of Swift 4 one by one, so that they can be displayed to you without reservation. in front of you.

1. Grammar improvement

extension can access private properties

Consider the following code:

struct Date: Equatable, Comparable {
    private let secondsSinceReferenceDate: Double
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

The above code defines a Date structure and implements the Equatable and Comparable protocols. In order to make the code clearer and more readable, the implementation of the protocol is generally placed in a separate extension, which is also a very Swift-style writing method, as follows:

struct Date {
    private let secondsSinceReferenceDate: Double
}
extension Date: Equatable {
    static func ==(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate == rhs.secondsSinceReferenceDate
    }
}
extension Date: Comparable {
    static func <(lhs: Date, rhs: Date) -> Bool {
        return lhs.secondsSinceReferenceDate < rhs.secondsSinceReferenceDate
    }
}

But in Swift 3, the compilation gives an error, because the secondsSinceReferenceDate property cannot be obtained in the extension, because it privateis . So in Swift 3, private must be changed to fileprivate.

struct Date {
    fileprivate let secondsSinceReferenceDate: Double
}
...

But if fileprivate is used, the scope of the attribute will be larger than we need, which may cause the misuse of the attribute accidentally.

In Swift 4, the scope of private properties is expanded to extension, and is limited to struct and extension, so there is no need to change to fileprivate, which is the best result.

Combination type of type and protocol

Consider the following code:

protocol Shakeable {
    func shake()
}

extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }

func shakeEm(controls: [???]) {
    for control in controls where control.state.isEnabled {
    }
    control.shake()
}

In Swift 3, what ???should be ? If it is written UIControl, it control.shake()will report an error; if it is written Shakeable, then control.state.isEnabledit will report an error. In fact, we can also write:

func shakeEm(controls: [UIControl]) {
    for control in controls where control.isEnabled {
        if control is Shakeable {
            (control as! Shakeable).shake()
        }
    }
}

Although it can be written in this way, it is very ugly.

In Swift 4, you can &combine as a single type, which can be written like this:

protocol Shakeable {
    func shake()
}

extension UIButton: Shakeable { /* ... */ }
extension UISlider: Shakeable { /* ... */ }

func shakeEm(controls: [UIControl & Shakeable]) {
    for control in controls where control.state.isEnabled {
        control.shake()
    }// Objective-C API
@interface NSCandidateListTouchBarItem<CandidateType> : NSTouchBarItem
@property (nullable, weak) NSView <NSTextInputClient> *client;
@end
}

Declare it as UIControl & Shakeabletype . OK, satisfactorily resolved.

PS:
This code example is in the PPT of WWDC 2017. The above code is a bit problematic. In the code of control.state.isEnabled, state does not have the attribute isEnabled, and control.isEnabledit . It seems that Apple's engineers are sometimes not very rigorous in making PPT.

In addition, the APIs in the iOS SDK are also optimized with this feature, for example:


The Objective-C version of this API is fine, knowing that the client property is both an NSView and conforms to the NSTextInputClient protocol. However its corresponding Swift 3 version is:

class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
    var client: NSView?
}

Just an NSView type/(ㄒoㄒ)/~~

In Swift 4, this API has been optimized and changed to:

class NSCandidateListTouchBarItem<CandidateType: AnyObject> : NSTouchBarItem {
    var client: (NSView & NSTextInputClient)?
}

This type of declaration is more rigorous.

Associated Type can append Where constraint statement

In Swift 4 you can append a where statement to the type declared after associatedtype

associatedtype Element where <xxx>

See below for the declaration of Element in Sequence in the Swift 4 standard library:

protocol Sequence {
    associatedtype Element where Self.Element == Self.Iterator.Element
    // ...
}

It defines that the type of Element in Sequence must be consistent with the type of Iterator.Element.

Through the where statement, you can add more constraints to the type to make it more rigorous and avoid redundant type judgment when using this type.

New Key Paths syntax

Let's first look at the writing of Key Paths in Swift 3:

@objcMembers class Kid: NSObject {
    dynamic var nickname: String = ""
    dynamic var age: Double = 0.0
    dynamic var friends: [Kid] = []
}

var ben = Kid(nickname: "Benji", age: 5.5)

let kidsNameKeyPath = #keyPath(Kid.nickname)

let name = ben.valueForKeyPath(kidsNameKeyPath)
ben.setValue("Ben", forKeyPath: kidsNameKeyPath)

Create a KeyPath \in starting with:

\Kid.nickname

When the compiler can deduce the type, the underlying type part can be omitted:

\.nickname

The above code can be written like this in Swift 4:

struct Kid {
    var nickname: String = ""
    var age: Double = 0.0
    var friends: [Kid] = []
}

var ben = Kid(nickname: "Benji", age: 8, friends: [])

let name = ben[keyPath: \Kid.nickname]
ben[keyPath: \Kid.nickname] = "BigBen"

Compared to Swift 3, Swift 4's Key Paths have the following advantages:

  1. Types can be defined as class, struct
  2. There is no need to add keywords such as @objcMembers, dynamic, etc. when defining a type
  3. better performance
  4. Type safety and type inference, for example, the ben.valueForKeyPath(kidsNameKeyPath)returned type is Any, and the ben[keyPath: \Kid.nickname]String type is returned directly
  5. Can be used on all value types

Subscripts support generics

Sometimes some data containers are written. Swift supports reading and writing data in the container through subscripts. However, if the data type in the container class is defined as generic, the previous subscript syntax can only return Any, which is required after retrieving the value. is used as?to convert types. Swift 4 can also use generics to define subscripts.

struct GenericDictionary<Key: Hashable, Value> {
    private var data: [Key: Value]
    
    init(data: [Key: Value]) {
        self.data = data
    }
    
    subscript<T>(key: Key) -> T? {
        return data[key] as? T
    }
}

let dictionary = GenericDictionary(data: ["Name": "Xiaoming"])

let name: String? = dictionary["Name"] // 不需要再写 as? String

2. String

Correctness of Unicode strings when calculating count improved

In Unicode, some characters are composed of several other characters, such as éthis character, which can \u{E9}be represented by , or by the combination of the e character and the above apostrophe \u{65}\u{301}.

Consider the following code:

var family = "

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325559702&siteId=291194637