似乎无法避免在 Swift 中强制展开

Can’t seem to avoid force unwrapping in Swift

更新:这是关于 Mac 使用 Xcode 中基于文档的应用程序模板创建的应用程序,我正在覆盖

override func readFromFileWrapper(fileWrapper: NSFileWrapper, ofType typeName: String, error outError: NSErrorPointer) -> Bool

我正在尝试从 NSFileWrapper 中读取一个文件,似乎我无法避免其中至少有一个 !

首先,我尝试了

if let rtfData = files["textFile.rtf"]?.regularFileContents,
        newString = NSMutableAttributedString(data: rtfData, options: [NSDocumentTypeDocumentAttribute : NSRTFTextDocumentType], documentAttributes: nil, error: nil) {
                text = newString
                return true
        }

所以我得到这个错误

Value of optional type 'NSData?' not unwrapped; did you mean to use '!' or '?'?

而且我必须强制转换 regularFileContents as NSData! 或者我必须在 NSMutableAttributedString 的初始化程序 (data: rtfData!) 中强制解包它。


然后我尝试了

let rtfData = files["textFile.rtf"]?.regularFileContents

if (rtfData != nil) {
    if let newString = NSMutableAttributedString(data: rtfData, options: [NSDocumentTypeDocumentAttribute : NSRTFTextDocumentType], documentAttributes: nil, error: nil) {
        text = newString
        return true
    }
}

这导致

Cannot find an initializer for type 'NSMutableAttributedString' that accepts an argument list of type '(data: NSData??, options: [String : String], documentAttributes: nil, error: nil)'"

所以我必须将初始化程序更改为 data: rtfData!!,我什至不确定 是什么意思

或者,我可以强制转换 regularFileContents as NSData?,然后我只能在初始化程序中使用一个 !

更新:这是自发布以来我尝试过的另一件事。我认为 NSData?? 中的双 ? 可能是由于我有选择地展开 NSFileWrapper (files["textFile.rtf"]?) 所以我尝试了这个:

if let rtfWrapper = files["textFile.rtf"],
    rtfData = rtfWrapper.regularFileContents,
    newString = NSMutableAttributedString(data: rtfData, options: [NSDocumentTypeDocumentAttribute : NSRTFTextDocumentType], documentAttributes: nil, error: nil) {
        text = newString
        return true
    }

编译器仍然要我强制解包 NSData。


我很困惑。

什么是NSData??,为什么它是“双重”可选的,为什么它是.regularFileContents的结果,我如何安全,而不!-一路走来,获取NSData并将其传递给NSMutableAttributedString的初始化器?


另一个更新

我认为 files 常量可能是另一个可选包装的来源:

let files = fileWrapper.fileWrappers

但是编译器不允许我 if let 它。例如,如果我尝试

if let files = fileWrapper.fileWrappers {
    if let rtfFile = files["textFile.rtf"] {
        if let rtfData = rtfFile.regularFileContents {
            if let newString = NSMutableAttributedString(data: rtfData, options: [NSDocumentTypeDocumentAttribute : NSRTFTextDocumentType], documentAttributes: nil, error: nil) {
                text = newString
                return true
            }
        }
    }
}

编译器报错:

Bound value in a conditional binding must be of Optional type

关于fileWrapper.fileWrappers

fileWrapper变量是方法的一个参数,即

override func readFromFileWrapper(fileWrapper: NSFileWrapper, ofType typeName: String, error outError: NSErrorPointer) -> Bool

你的线路:

files["textFile.rtf"]?.regularFileContents

Returns 双重包装可选。一种用于查字典,一种用于.regularFileContents。当您执行 if let 时,您解包了一次:这就是为什么您的错误是:

// Value of optional type 'NSData?' not unwrapped; did you mean to use '!' or '?'?

但是,在你的第二个版本中,如果 rtfData 为 nil,你只需 检查 - 你不打开它:

if (rtfData != nil)

这为您提供了双重包装选项。

绕过它的最简单方法是使用 flatMap:

if let rtfData = files["textFile.rtf"].flatMap({[=13=].regularFileContents}) {
    text = NSMutableAttributedString(data: rtfData, options: [NSDocumentTypeDocumentAttribute : NSRTFTextDocumentType], documentAttributes: nil, error: nil)
    return true
}

flatMap 接受一个可选的,并将其解包以将其放入 returns 一个可选的函数中。如果原始可选值或函数返回的值的计算结果为 nil,则整个事物的计算结果为 nil。但是它的值 returns 只是单独包装的。

flatMap所做的基本上是:

if let lookup = files["textFile.rtf"] {
  if let rtfData = lookup.regularFileContents {
    let newString = NSMutableAttributedString(data: rtfData, options: [NSDocumentTypeDocumentAttribute : NSRTFTextDocumentType], documentAttributes: nil, error: nil) 
    text = newString
    return true
  }
}

这段代码也可以。两种方法都可以安全地解包 rtfData 两次。

您可以先对文件使用可选绑定,然后从那里继续:

if let file = files["textFile.rtf"],
    contents = file.regularFileContents,
    newString = NSMutableAttributedString(data: contents, options: [NSDocumentTypeDocumentAttribute : NSRTFTextDocumentType], documentAttributes: nil, error: nil) {
        text = newString
        return true
}

如果您使用的是 fileWrappers 词典([NSObject : AnyObject]),则必须进行一些额外的转换。这个例子使用的是我碰巧在那个文件包装器中的文件,但希望它能说明这个想法:

var text: NSString!

func someMethod(fileWrapper: NSFileWrapper) {
    let files = fileWrapper.fileWrappers

    if let file = files["test.json"] as? NSFileWrapper,
        contents = file.regularFileContents,
        newString = NSString(data: contents, encoding: NSUTF8StringEncoding)  {
            text = newString
            println(text)
    }
}