Swift Burning Brain Gymnastics One

Source: Internet
Author: User
Tags delete key unpack

Swift Burning Brain Gymnastics (i)-Optional nesting 

Preface

Swift is actually much more complex than objective-c, and Swift incorporates a number of new features compared to Objective-c, born in the 80 's. It also makes it more difficult for us to learn to master the language. But everything is worth it, Swift compared to Objective-c, the program written out more safe, more concise, and ultimately improve our efficiency and quality.

There are a lot of Swift-related learning materials, and I want to introduce some of its features from another perspective, which I call "brain-burning gymnastics." What do you mean? Is that we specialize in some of the more cost-brain language details to learn. To achieve a more in-depth understanding of the Swift language through "burning the brain" to think.

This is the first section of gymnastics, please be ready to exercise before practicing, keep your mind awake.

Preparation Exercise: Introduction to Optional

Wang Wei's "Swifter" (Http://swifter.tips/buy), describes a useful command: Enter in the Lldb fr v -R foo , you can see foo the memory composition of this variable. We will use this command later in our analysis.

In Swift's world, everything is object, including the basic data types of Int Float, so we can write this: print(1.description) .

While objects are generally stored in pointers, Swift is no exception, which creates a problem where pointers are empty and need to be handled. In Objective-c, sending a message to a nil object is a default behavior that does not produce any effect, but in Swift, this behavior is strictly forbidden.

Swift is a strongly typed language that wants to do more security checks at compile time, so it introduces type inference. It is a basic requirement to avoid NULL pointer invocation if you want to do enough security on the type inference. So, this type of Optional appears. Optional is actually an enumeration type in the Swift language:

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {    case None    case Some(Wrapped)}
Nesting of Optional

Optional types of variables, in use, most need to use if let the way to unpack. If you do not unpack and use it directly, the editor will prompt you with type inference, so it seems that this mechanism works very well. However, if the Optional nesting level is too many, it will cause some trouble, let's look at an example below.

let a: Int? = 1let b: Int?? = alet c: Int??? = b

In this mechanism, the INT value of 1 is Optional wrapped by layers, and we fr v -R can see the internal structure very well with what we just mentioned. Such as:

(lldb) fr v -R a(Swift.Optional<Swift.Int>) a = Some {  Some = {    value = 1  }}(lldb) fr v -R b(Swift.Optional<Swift.Optional<Swift.Int>>) b = Some {  Some = Some {    Some = {      value = 1    }  }}(lldb) fr v -R c(Swift.Optional<Swift.Optional<Swift.Optional<Swift.Int>>>) c = Some {  Some = Some {    Some = Some {      Some = {        value = 1      }    }  }}

From this example code, we can see the specific memory structure of a multilayer nested Optional. This memory structure is actually a binary tree-like shape, as shown in:

    • The first-level binary tree has two optional values, one value is. None, and the other value type is Optional<Optional<Int>> .

    • The second-level binary tree has two optional values, one value is. None, and the other value type is Optional<Int> .

    • The third-tier binary tree has two optional values, one value is. None, and the other value type is Int .

So the question is, it looks like this optional.none can appear on every layer, so what's the effect on each layer? I did the following experiment:

let a: Int? = nillet b: Int?? = alet c: Int??? = blet d: Int??? = nil

If you look at playground, their values are nil, but their memory layout is different, especially the variable C and variable D:

   (lldb) Fr V-r A (swift.optional<swift.int>) a = None { some = {&nbs P  value = 0  }} (Lldb) FR V-r B (swift.optional<swift.optional<swift.int>>) b = Some { Some = No ne {   some = {     value = 0    }  }} (Lldb) Fr v-r C (SWIFT.OPTIONAL<SWIFT.O ptional<swift.optional<swift.int>>>) c = Some { some = Some {   some = None {  &NBSP ;  some = {       value = 0      }    }  }} (Lldb) Fr V-r D (swift.op tional<swift.optional<swift.optional<swift.int>>>) d = None { some = Some {   Some = so Me {     some = {       value = 0      }    }  }}
    • Variable c because it is a multi-layered nested nil, so it is the value on the outermost two-fork tree, is one Optional<Optional<Int>> .

    • The variable d is a value that is directly assigned to nil, so it is on the outermost two-fork tree Optional.None .

The trouble has come, the above reasons will cause to use if let to determine whether the variable C is nil failure. The following code will eventually output c is not none .

let a: Int? = nillet b: Int?? = alet c: Int??? = blet d: Int??? = nilif let _ = c {    print("c is not none")}
explain

In my opinion, the root of the problem is that a variable of type Optional can accept a non-Optional value. Take the above code example, the type of a is the int?,b type is Int???, but the value of a can be assigned to B. So, variable B (type Int??). ), it can accept the following types of assignments:

    1. Nil type

    2. Int? Type

    3. Int?? Type

In a word, Swift is a strong type, and the right and left sides of the equals sign are not exactly the same type, why is it possible to assign success? I checked the Optional source, originally for the above 1th, 2 kinds of different types of cases, Optional defined the constructor to construct an Int?? Type, so that after construction, the equals sign is the same as the left and right sides. Source code from Https://github.com/apple/swift/blob/master/stdlib/public/core/Optional.swift, I excerpt as follows:

public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {  case None  case Some(Wrapped)  @available(*, unavailable, renamed="Wrapped")  public typealias T = Wrapped  /// Construct a `nil` instance.  @_transparent  public init() { self = .None }  /// Construct a non-`nil` instance that stores `some`.  @_transparent  public init(_ some: Wrapped) { self = .Some(some) }}

In the above code, Optional provides two constructors to complete the type conversion work just mentioned.

Brain-Burning gymnastics

Well, with all that said, we're starting to burn down, and the following code is from Fujo (Https://github.com/lingoer) at the recent Swift conference (http://atswift.io/#speaker) to share:

var dict :[String:String?] = [:]dict = ["key": "value"]func justReturnNil() -> String? {    return nil}dict["key"] = justReturnNil()dict

The following is the result of the code execution:



We can see that we want to remove this key-value pair by setting a nil for this Dictionary. However, from playground's execution results, key has not been deleted.

In order to test exactly what value to set, in order to properly delete the Key-value key value pair, I did the following experiment:

var dict :[String:String?] = [:]// first trydict = ["key": "value"]dict["key"] = Optional<Optional<String>>.Nonedict// second trydict = ["key": "value"]dict["key"] = Optional<String>.Nonedict// third trydict = ["key": "value"]dict["key"] = nildict// forth trydict = ["key": "value"]let nilValue:String? = nildict["key"] = nilValuedict// fifth trydict = ["key": "value"]let nilValue2:String?? = nildict["key"] = nilValue2dict

The results of the implementation are as follows:

As we can see, the following three ways to successfully delete Key-value key-value pairs:

    • dict["key"] = Optional<Optional<String>>.None

    • dict["key"] = nil

    • let nilValue2:String?? = nil;  dict["key"] = nilValue2

So, in this journey of burning the brain, we find that a Dictionary of [string:string?] can accept the following types of assignments:

    • Nil

    • String

    • String?

    • String??

If you want to delete an element in this Dictionary, you must pass in nil or Optional<Optional<String>>.None , if you pass in Optional<String>.None , you cannot delete the element normally.

Well, what is the cause of this phenomenon?

Fortunately, Apple has its implementation open source, then let's take a look at it, source files from: https://github.com/apple/swift/blob/master/stdlib/public/core/ HashedCollections.swift.gyb, here is the key code.

  public subscript(key: Key) -> Value? {    get {      return _variantStorage.maybeGet(key)    }    set(newValue) {      if let x = newValue {        // FIXME(performance): this loads and discards the old value.        _variantStorage.updateValue(x, forKey: key)      }      else {        // FIXME(performance): this loads and discards the old value.        removeValueForKey(key)      }    }  }

So, when Dictionary's value type is string, if you want to set its value, does it accept a string? The parameters of the type. And because the value type in the example we just had is string, so what normally it needs is a string?? The parameters of the type. In the above example of failure, we are passing a String? The value of the type, which is Optional<String>.None then performed as follows:

    1. We pass a value that is a Optional<String>.None parameter of type String?.

    2. Because the argument type passed is string, and the function needs a string??, the Optional constructor is executed, and a two-layer Optional is constructed.

    3. The value of this two-layer Optional isOptional.Some(<Optional<String>.None>)

    4. When entering the implementation of Dictionary, the If let is used to determine whether nil, because the two layers of Optional, so if let judge it is not nil.

    5. So the code executes to _variantStorage.updateValue(x, forKey: key) , put Optional

      . None as a value, set to the corresponding key.

If you don't understand, you can turn over the original experiment and analysis of the multi-layer nested nil variable.

Let's take a look at the case where the pass parameter is Optional<Optional<String>>.None , the following steps:

    1. We pass a value that is a Optional<Optional<String>>.None parameter of type String??.

    2. Because the parameter type is a string??, the type required by the function is also a string??, so the parameter is not transformed and goes directly into the function call.

    3. This time the value of the parameter is not changed, or Optional<Optional<String>>.None .

    4. When entering the implementation of Dictionary, the If let is used to determine whether nil, and Optional<Optional<String>>.None if let judge, get it is nil.

    5. So the code executes to removeValueForKey(key) , Dictionary delete the corresponding Key-value key value pair.

Summary

Well, the first section of "Brain-burning gymnastics" is done, exercise is not feel refreshed?

Summarize the brain cells that are burned to the brain:

    • Optional can be nested in multiple layers.

    • Because Optional's constructor supports it, you can assign a value of type T to a type T? The variable.

    • Because Optional's constructor supports it, you can assign nil to a Optional variable of any number of nested layers.

    • When Optional nested content is nil, you should be careful if let operation fails.

    • Multi-layered Optional are prone to burn brain cells and avoid being used or triggered in engineering.

    • Encounter problems can be turned over Apple in Github Open source Swift source code.

May everyone have fun!

Swift Burning Brain Gymnastics One

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.