The optional type used in Swift perfectly solves the placeholder problem

  • 2020-05-10 22:59:10
  • OfStack

The optional type is a powerful new addition to Swift. In this blog post, we discuss how to ensure strongly typed security with optional types in Swift. As an example, let's create an Swift version of Objective-C API, but Swift itself doesn't need such an API.

Add the objectsForKeys function to Dictionary

In Objective-C, NSDictionary has one method, objectsForKeys:NoFoundMarker:, which takes an NSArray array as a key-value argument and returns an array containing the relevant values. The document says, "return the N value in the array, corresponding to the N value in the input array." what if there is a key value that does not exist in the dictionary? So you have notFoundMarker as the return prompt. Such as the third key value is not found, then the third in the returned array is that the notFoundMarker, rather than the third values in the dictionary, but this value is used to remind the original didn't find the corresponding value in the dictionary, but in return the elements in the array, and use notFoundMarker as a placeholder, because the object cannot be used directly, so in the frame of the Foundation has a special class to handle this situation: NSNull.

In Swift, the Dictionary class does not have a function similar to objectsForKeys, so to illustrate, let's add one and make it a common way to manipulate dictionary values. We can use extension to achieve:


extension Dictionary{
    func valuesForKeys(keys:[K], notFoundMarker: V )->[V]{
        // The implementation code will be written later
    }
}

So that's the Swift version that we implemented, which is very different from the Objective-C version. In Swift, we cannot put NSNull in a string array because it is strongly typed, limiting the return result array to only single-type 1 elements. However, Swift has a better option, which is to return an optional type of data. All of our values are wrapped in optional types, not NSNull, we just use nil.

extension Dictionary{
    func valuesForKeys(keys: [Key]) -> [Value?] {
        var result = [Value?]()
        result.reserveCapacity(keys.count)
        for key in keys{
            result.append(self[key])
        }
        return result
    }
}

A simpler approach in Swift

Why, you might ask, is it not necessary to implement such an API in Swift? There is a simpler implementation, as shown in the following code:


extension Dictionary {
    func valuesForKeys(keys: [Key]) -> [Value?] {
        return keys.map { self[$0] }
    }
}

The above method implements the same functionality as the original method, although the core function is to encapsulate the call to map. This example also shows why Swift does not provide a lightweight API interface, because it can be implemented by simply calling map.

Next, let's experiment with a few examples:


var dic: Dictionary = [ "1": 2, "3":3, "4":5 ] var t = dic.valuesForKeys(["1", "4"])
// The result is: [Optional(2), Optional(5)] var t = dict.valuesForKeys(["3", "9"])
// The result is: [Optional(3), nil] t = dic.valuesForKeys([])
// The result is: []

Inline optional types

Now, if we call the last method for every result, what happens?


var dic: Dictionary = [ "1": 2, "3":3, "4":5 ] var t = dic.valuesForKeys(["1", "4"]).last // The result is: Optional(Optional(5))
// Optional(Optional("Ching")) var t = dict.valuesForKeys(["3", "9"]).last
// The result is: Optional(nil) var t = dict.valuesForKeys([]).last
// The result is: nil

People immediately wondered why there were two layers of optional types. , Optional(nil) in case 2, what is the rhythm?

Let's go back to the definition of the last property:


var last:T? { get }

It is clear that the type of the last attribute is an optional type for the array element type, in which case, because the element type is (String?) , then combine the returned type, and the result is String? The & # 63; Well, this is called nested optional types. But what does the nature of nested optional types mean?
If we call the above method again in Objective-C, we will use NSNull as the placeholder, and the call syntax for Objective-C is as follows:

[dict valuesForKeys:@[@"1", @"4"] notFoundMarker:[NSNull null]].lastObject
// 5
[dict valuesForKeys:@[@"1", @"3"] notFoundMarker:[NSNull null]].lastObject
// NSNull
[dict valuesForKeys:@[] notFoundMarker:[NSNull null]].lastObject
// nil

Whether it's the Swift version or the Objective-C version, a return value of nil means that the array is empty, so it doesn't have the last element. However, if the return is Optional(nil) or NSNull in Objective-C, the last element in the array exists, but the contents of the element are empty. Objective-C can only use NSNull as a placeholder for this purpose, but Swift can be implemented from a language system type perspective.
Provide a default value
Step 1 encapsulation. What if one or more elements in my dictionary do not exist and we want to provide a default value? The implementation is simple:

extension Dictionary {
    func valuesForKeys( keys:[Key], notFoundMarker: Value)->[Value]{
        return self.valueForKeys(kes).map{ $0 ?? notFoundMarker }
    }
}
dict.valuesForKeys(["1", "5"], notFoundMarker: "Anonymous")

In contrast to Objective-C, which requires placeholders for placeholder purposes, Swift has native support for this usage at the level of the language type system and provides rich syntactic functionality. This is the power of the Swift optional type. Note also the use of the null operator ? The & # 63; .


Related articles: