Быстрая привязка к необязательному назначению

Я пишу код в Swift для изучения языка. Вот мой базовый класс:

import Foundation
class BaseCommand:NSOperation
{
    var status:Int? = nil
    var message:String? = nil

    func buildRequest() -> NSData?
    {
        return nil
    }

    func parseResponse(data:NSData?) -> (Status:Int, Error:String)
    {
        return (200, "Success")
    }

    override func main() {
        let requestBody = self.buildRequest()

        println("Sending body \(requestBody)")
        // do network op
        var networkResultBody = "test"

        var resultBody:NSData = networkResultBody.dataUsingEncoding(NSUTF8StringEncoding)!
        (self.status, self.message) = self.parseResponse(resultBody)
    }
}

Проблема находится в последней строке:

(self.status, self.message) = self.parseResponse(resultBody)

Компилятор говорит: "Невозможно выразить преобразование кортежа (Status: Int, Error: String) в (Int?, String?)"

Я понимаю, что проблема в том, что self.status и self.message являются опциями, и parseResponse не возвращает Optionals (и я не хочу этого). Как я могу сказать, что нужно выполнить назначение и преобразование, чтобы получить данные в переменные экземпляра?

Ответ 1

Еще один ответ предложил (до его изменения):

(self.status!, self.message!) = self.parseResponse(resultBody)

Я обнаружил, что это небезопасно. Он будет сбой, если во время назначения или self.status или self.message будет nil. Попробуйте этот тест на игровой площадке:

class Test {
    var status: Int?
    var error: String?

    func parseResponse() -> (Status:Int, Error:String)
    {
        return (200, "Success")
    }

    func testIt() {
        (self.status!, self.error!) = parseResponse()
        print(self.status)
        print(self.error)
    }
}

let mytest = Test()
mytest.testIt()

Вот еще один способ:

let (stat, err) = self.parseResponse(resultBody)
(self.status, self.error) = (Optional(stat), Optional(err))

или, как обнаружил @AndriyGordiychuk, он работает без Optional:

let (stat, err) = self.parseResponse(resultBody)
(self.status, self.error) = (stat, err)

Любопытно, что это работает, но назначение результата функции не выполняется.


Обратите внимание на следующий эксперимент:

var i: Int?
var s: String?

// This works
(i, s) = (3, "hello")

// This fails with error: cannot express tuple conversion '(Int, String)' to '(Int?, String?)
let t = (3, "hello")
(i, s) = t

Похоже, что когда присваивание является строковым кортежем, назначенным кортежу, Swift принимает ярлык и не строит сначала кортеж. Вместо этого просто назначается отдельный элемент.

Итак, это:

(i, s) = (3, "hello")

эквивалентно:

i = 3
s = "hello"

который работает, потому что вы можете назначить Int переменной Int? и переменной String to String?. Назначение кортежа не выполняется, поскольку типы должны соответствовать.

Ответ 2

Вы должны сделать это (необязательные необязательные):

let returnValue = self.parseResponse(resultBody)
(self.status,self.message) = (returnValue.Status,returnValue.Error)