web-dev-qa-db-fra.com

Swift paramètres optionnels inout et nil

Est-il possible d'avoir un paramètre Optionalinout pour une fonction dans Swift? J'essaie de faire ceci:

func testFunc( inout optionalParam: MyClass? ) {
    if optionalParam {
        ...
    }
}

... mais quand j'essaie de l'appeler et que je transmets nil, cela me donne une étrange erreur de compilation:

Type 'inout MyClass?' does not conform to protocol 'NilLiteralConvertible'

Je ne vois pas pourquoi ma classe devrait être obligée de se conformer à un protocole spécial alors qu'elle est déjà déclarée optionnelle.

17
devios1

Il ne compilera pas car la fonction attend une référence mais vous avez passé nil. Le problème n'a rien à voir avec facultatif.

En déclarant paramètre avec inout signifie que vous allez lui attribuer une valeur dans le corps de la fonction. Comment peut-il affecter une valeur à nil?

Vous devez l'appeler comme

var a : MyClass? = nil
testFunc(&a) // value of a can be changed inside the function

Si vous connaissez le C++, il s'agit d'une version C++ de votre code sans option

struct MyClass {};    
void testFunc(MyClass &p) {}
int main () { testFunc(nullptr); }

et vous avez ce message d'erreur

main.cpp:6:6: note: candidate function not viable: no known conversion from 'nullptr_t' to 'MyClass &' for 1st argument

ce qui est un peu équivalent à ce que vous avez (mais plus facile à comprendre)

26
Bryan Chen

C'est possible, et cela peut avoir tout à voir avec Closure . ()

L'option optionnelle peut avoir une valeur nulle, mais elle ne s'attend pas à zéro. Donc, l’option optionnelle fonctionnerait si vous ne la passiez pas à la fonction ou si vous passiez une variable de type MyClass? avec une valeur nulle. Comme le dit Bryan.

Lorsque vous écrivez la fonction, sa valeur par défaut doit être un paramètre facultatif, comme ceci:

func testFunc(inout optionalParam:MyClass?={var nilRef:MyClass?;return &nilRef}()) {
    if optionalParam != nil {
        ...
    }
}

Avis if optionalParam {...} est remplacé par if optionalParam != nil {...} 

vous ne pouvez PAS utiliser la variable optionalParam sans la cocher, c'est-à-direif optionalParam? != nil, comme encapsulage, optionalParam? échoue lorsque optionalParam==nil. ~ note, var a :MyClass? a la valeur nil jusqu'à ce qu'il soit affecté.

L'appel est soit, testFunc(optionalParam:&a) ou testFunc(), mais jamais testFunc(nil)

Pensez que vous pouvez associer deux concepts distincts car ils portent des noms similaires, des paramètres et des options facultatifs. Les options sont des variables avec une condition nulle étendue. Les paramètres facultatifs sont des paramètres de fonction qui sont facultatifs.

Lectures complémentaires ici . Apple tente de changer le verbiage de paramètre facultatif en "paramètres avec valeurs par défaut". Il est regrettable qu’ils n’aient pas incorporé le comportement des paramètres facultatifs dans ces nouvelles options. Quel est l'intérêt de passer à zéro les options comme des options si elles échoueront une fois déballées. Peut-être que cela devient plus profond au cœur de cette chose optionnelle si elle peut être passée avant de déballer ... Le chat de Schrodinger

3
izzy

Le passage exact du document est:

Vous ne pouvez transmettre qu'une variable en tant qu'argument pour un paramètre in-out. Vous ne pouvez pas transmettre une constante ou une valeur littérale en tant qu'argument, car les constantes et les littéraux de Ne peuvent pas être modifiés. Vous placez un esperluette (&) Directement devant le nom de la variable lorsque vous le transmettez en tant qu’argument à Un paramètre inout, pour indiquer qu’il peut être modifié par la fonction .

Notez cependant que (édité) par le commentaire de @ gnasher, il vous suffit de passer une classe comme inout (ou par référence), si vous souhaitez ou autorisez le remplacement de l'instance par une autre instance, et non pas simplement l'original modifié. Le passage dans la documentation qui couvre ceci est:

Paramètres In-Out

Les paramètres variables, tels que décrits ci-dessus, ne peuvent être modifiés que dans La fonction elle-même. Si vous souhaitez qu'une fonction modifie la valeur D'un paramètre et que ces modifications soient conservées après la fin de l'appel de la fonction , Définissez ce paramètre en tant que paramètre in-out.

Voici trois tests qui couvrent l’utilisation de var et inout.

class Bar : Printable {
    var value = 1

    init(_ value:Int) { self.value = value }
    var description:String { return "Bar is: \(value)" }
}

let bar = Bar(1)
func changeBarWithoutInoutSinceBarIsAClassYaySwift(b:Bar) { b.value = 2 }
changeBarWithoutInoutSinceBarIsAClassYaySwift(bar)
println("after: \(bar)") // 2

var bar2 = Bar(0)
func tryToReplaceLocalBarWithoutInout(var b:Bar) { b = Bar(99) }
tryToReplaceLocalBarWithoutInout(bar2)
println("after: \(bar2)") // 0

var bar3 = Bar(0)
func replaceClassInstanceViaInout(inout b:Bar) { b = Bar(99) }
replaceClassInstanceViaInout(&bar3)
println("after: \(bar3)") // 99
2
Chris Conover

En fait, ce dont @ devios1 a besoin est un "pointeur facultatif". Mais inout MyClass? signifie "pointeur sur une option". Ce qui suit devrait fonctionner dans Swift 4

class MyClass {
    // func foo() {}
}

func testFunc(_ optionalParam: UnsafeMutablePointer<MyClass>? ) {
    if let optionalParam = optionalParam {
        // optionalParam.pointee.foo()
        // optionalParam.pointee = MyClass()
    }
}

testFunc(nil)

var myClass = MyClass()
testFunc(&myClass)
0
Slavko Coolesoff