从闭包委托给另一个初始化程序

时间:2017-09-28 06:04:07

标签: swift initialization

我正在研究如何从DispatchData实例中创建Data实例。我开始使用静态函数:

static func dispatchData(fromData data: Data) -> DispatchData {
    return data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) -> DispatchData in
        let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
        return DispatchData(bytes: bufferPtr)
    }
}

这很好(并且恰巧与this answer非常相似)。然后我决定将其作为初始化程序添加到扩展名中的DispatchData并写道:

private extension DispatchData {
    init(data: Data) {
        data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in  // 1
            let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
            self.init(bytes: bufferPtr)
        }
    }
}

此时,编译器拒绝标记为// 1

的行
  

在初始化之前由闭包捕获的变量'self .__ wrapped'

这是有道理的 - 编译器不希望在我委托给self之前捕获init(bytes: UnsafeRawBufferPointer),因为存储的变量 - 如__wrapped,在这种情况下 - 不是'尚未初始化。但我看不出另一种获得UnsafeRawBufferPointer的方式;根据{{​​3}}:

,我无法从data.withUnsafeBytes返回
  

警告

     

不应在闭包调用的生命周期之外存储和使用字节指针参数。

我知道我可以回到使用静态函数来实现我想要的东西,但如果有办法添加这个初始化程序,我更喜欢它。有没有办法让这个初始化程序按预期工作?

1 个答案:

答案 0 :(得分:3)

你的扩展的问题在于,尽管编译器知道你将关闭作为非转义参数传递给withUnsafeBytes(_:);它没有任何保证它将被调用一次而一次(这是初始化所必需的。)

一个简单的解决方案是,而不是链接到另一个初始化器,只需自己构造一个DispatchData的新实例,然后将其分配给self,从而完成初始化:

import Foundation

private extension DispatchData {
  init(data: Data) {
    self = data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in
      DispatchData(bytes:
        UnsafeRawBufferPointer(start: typedPtr, count: data.count)
      )
    }
  }
}

另请注意,将UnsafePointer<UInt8>传递给UnsafeRawPointer?时,会将其作为参数传递;所以我们不需要将它包装在初始化调用中。

在Swift 5中,withUnsafeBytes直接为您提供UnsafeRawBufferPointer

private extension DispatchData {
  init(data: Data) {
    self = data.withUnsafeBytes(DispatchData.init)
  }
}