Optional chaining的秘密(二)

八条8tiaoIP属地: 吉林
字数 878阅读 1,122

作为一名程序员,我们是不会甘心止步于语法的“甜”,我们有永恒的动力去揭开这层语法的外衣一看究竟:)。我们在《Optional chaining的秘密(一)》中指出了,在类型的后面使用?表示Optional类型,而?.则是map 方法的语法糖。

我们今天要研究的是在类型的后面使用!意味着什么?

ImplicitlyUnwrappedOptional

只要我们随便翻翻官方文档,我们就能知道,下面这段代码是编译不过的

let x: String? = "Hello,world"
func printString(str: String) {
    print(str)
}
printString(x) // error!!!!!!

原因就在于 xString? 声明为一个Optional<String>类型了,而方法printString()要求的参数是 String 类型,所以如果直接传递 x 会导致调用失败,如果我们希望方法调用成功,那么我们需要这样处理。

printString(x!)

我们需要使用 ! 强制对 x 进行拆封,使其还原为 String 类型。那么如果 ! 放在类型的后面意味着什么呢?

let x: String! = "Hello,world"
func printString(str: String) {
    print(str)
}
printString(x) // ok!!!!!!

文档把这种 let x: String! 称为隐式拆封(implicitly unwrapped),在 implicitly unwrapped 的帮助下,我们可以看到printString(x)中的参数x不需要强制拆封了。难道let x: String!为我们定义了一个String类型?no,no,no,no

我们在看一个长一点的例子。

public class Demo {
    public var subDemo: SubDemo! = nil
}

public class SubDemo {
    public let count: Int = 1
}

let demo: Demo! = Demo()
demo.subDemo = SubDemo()

let count = demo.subDemo.count

在这个代码片段中,我们有两处值得注意:

  • public var subDemo: SubDemo! = nil可以被声明为nil
  • let count = demo.subDemo.count 可以chaining式调用

第一点是比较容易解释的,subDemo之所以可以被声明为nil是因为,SubDemo!声明了一个ImplicitlyUnwrappedOptional类型,也就是说它不是一个普通的类型,如果它是一个普通的SubDemo类型,那么它一定不允许为nil.

如何完成链式的调用

那怎么解释第二点,链式调用呢?让我们循序渐进,我们现在有这样个方法,可以给一个string做扩展

extension String {
    static func appendingString(str: String) -> (String) -> String {
        return { str + $0 }
    }
}

var aStr = "Hello"
var bString = String.appendingString(aStr)("!")
var method = String.appendingString(aStr)
var cStr = method("!")

现在我们定义个操作符方法:+- 它可以帮助我们实现上面同样的功能

infix operator +- { associativity left }
func +-<T,Z>(obj:T, f:T->Z) -> Z {
    return f(obj)
}

通过这个中缀操作符的定义,我们可以这样实现给一个字符做扩展的功能。

let helloString = "hello" +- {
    String.appendingString($0)("!")
}

操作符 +- 接收两个参数,一个是字符串"hello",另外一个是闭包。而且通过associativity的声明,这种操作可以还可以产生一个连缀式调用。

let helloString = "hello" +- {
    String.appendingString($0)("!")
    } +- { String.appendingString($0)("!") }

说了这么多,不知道我们又没有注意,我们现在已经可以对 String 类型做类似 ?. 操作了,只是当前类型是String,我们现在要将其适用于ImplicitlyUnwrappedOptional类型。

infix operator +- { associativity left  }
func +-<T,Z>(obj:ImplicitlyUnwrappedOptional<T>, f:T->Z) -> ImplicitlyUnwrappedOptional<Z> {
    switch obj {
    case .Some(let x):
        return f(x)
    case .None:
        fatalError()
    }
}

我们重新实现操作符 +- 的定义,使其接收一个ImplicitlyUnwrappedOptional类型的参数并返回一个ImplicitlyUnwrappedOptional类型的值。方法体内容会对参数obj进行拆包,如果为nil则会报错,否则经过转换后返回ImplicitlyUnwrappedOptional,这不就是完整的implicitly unwrapped的工作过程吗?好,现在我们把String声明为implicitly unwrapped

var oldString: String! = "hello"  // String!

let helloString = oldString +- {
    String.appendingString($0)("!")
    } +- { String.appendingString($0)("!") }

这就是Optinal chainingimplicitly unwrapped 是的工作过程。

总结

以上代码在xcode 7.3上都测试通过。虽然代码没有swift的链式操作符代码那样漂亮,但这些功能确实可以通过纯swift代码来实现。希望这些内容有助于大家对optional更深入的理解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
5人点赞
更多精彩内容,就在简书APP
"小礼物走一走,来简书关注我"
还没有人赞赏,支持一下
总资产2共写了3.5W字获得193个赞共151个粉丝

推荐阅读更多精彩内容

  • 基础部分(The Basics) 当推断浮点数的类型时,Swift 总是会选择Double而不是Float。 结合...
    gamper阅读 1,352评论 0 7
  • 对各种值为"空"的情况处理不当,几乎是所有Bug的来源。 在我们的例子里,尽管tmp的值是nil,但调用tmp的r...
    AKyS佐毅阅读 10,572评论 1 13
  • Swift 是一门开发 iOS, macOS, watchOS 和 tvOS 应用的新语言。然而,如果你有 C 或...
    XLsn0w阅读 934评论 2 1
  • 聊聊费兰克的写作训练营 近来喜欢上了写作,而且想提高一下写作水平,又偶然知道了一个叫做费兰克的家伙花了十多万去学习...
    简单国平阅读 442评论 2 49
  • 江梅从一片混沌中醒来的时候,厚重的遮光窗帘严实地挡住了窗外的景象,因此她也无法辨认此刻到底是白昼还是黑夜。 黑暗中...
    _云本_阅读 255评论 0 1