2016-08-05 14 views
7

Zuerst versuche ich eine [String?] Kartierung, erhalten eine [String]:Warum gibt Swift null-coalescing ein Optional zurück?

$ xcrun swift 
Welcome to Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.30). Type :help for assistance. 
    1> import Foundation 
    2> let j: [String?] = ["a", nil] 
j: [String?] = 2 values { 
    [0] = "a" 
    [1] = nil 
} 
    3> j.map {$0 ?? ""} 
$R0: [String] = 2 values { 
    [0] = "a" 
    [1] = "" 
} 

Dies macht Sinn für mich. Ich füge kein String? zusammen, und ich bekomme String. Aber mit [AnyObject?], etwas seltsam vorkommt:

4> let k: [AnyObject?] = ["a", nil] 
k: [AnyObject?] = 2 values { 
    [0] = "a" 
    [1] = nil 
} 
    5> k.map {$0 ?? ""} 
$R1: [AnyObject?] = 2 values { 
    [0] = "a" 
    [1] = (instance_type = 0x00007fff7bc2c140 @"") 
} 

Ich bin Null-Koaleszenz optionals, aber dieses Mal habe ich eine optionale raus. Warum?

Die Swift Programming Language sagt a ?? b Abkürzung für a != nil ? a! : b ist, aber wenn ich versuche, dass ich aus einer Reihe von Nicht-optionals:

6> k.map {$0 != nil ? $0! : ""} 
$R2: [AnyObject] = 2 values { 
    [0] = "a" 
    [1] = "" 
} 

Bin ich Missverständnis, wie ?? funktionieren soll? Was geht hier vor sich?

+2

Interessante Frage, aus dem gleichen Grund scheint dies zu funktionieren 'let res: [AnyObject] = k.map {$ 0 ?? ""} ' –

+2

Sieht aus wie ein komisches Inferenz-Problem mit' AnyObject' und Literalen - das funktioniert auch: 'let k1 = k.map {$ 0 ?? String()} ' – Hamish

Antwort

0

Es hat meine Aufmerksamkeit gekommen, dass Apple in Swift 2. dies einen Fehler betrachtet

In Swift 3, noch das erste Beispiel oben arbeitet, während die 2. und 3. Beispiele ungültige Syntax sind (mit oder ohne Foundation Bridging).

Ersetzen der AnyObject Erklärung mit Any Werke: a ?? b dann verhält sich identisch zu a != nil ? a! : b, wie die Dokumentation sagt.

2

Das detaillierte Verhalten ist nicht gut dokumentiert, so würde sich in Zukunft Swifts ändern.

Aber Sie sollten wissen Bediener hat zwei Überlastungen koaleszierende:

@warn_unused_result 
public func ??<T>(optional: T?, @autoclosure defaultValue:() throws -> T) rethrows -> T 

@warn_unused_result 
public func ??<T>(optional: T?, @autoclosure defaultValue:() throws -> T?) rethrows -> T? 

In Ihrem Fall hat Swift letzteres für Ihren Code ausgewählt.

Sie können wie mit einer vereinfachten Codes testen:

let x: AnyObject? = "a" 
x ?? "" 

der abgeleiteten Typ (in Swift 2.2.1) AnyObject? wird. Aber dieser Code ist auch gültig.

let y: AnyObject = x ?? "" 

Stringliterale wie "" kann als Vielzahl von Arten behandelt werden. Alle diese sind in Swift gültig.

"" as String 
"" as String? 
"" as NSString 
"" as NSString? 
"" as AnyObject 
"" as AnyObject? 

Also, mit einem unbestimmten Grund hat Swift AnyObject? gewählt. Und falls die Typrückschlüsse mehrdeutig sein können, sollten Sie die explizite Typ-Anmerkung verwenden, wie im Kommentar von appzYourLife vorgeschlagen.

+0

Interessant! Es sieht also so aus, als ob kein Koaleszieren in * Swift definiert ist, d. H. Es ist nicht nur eine spezielle Syntax. Aber der ternäre Operator ist eine spezielle Syntax, oder? Bedeutet dies, dass die Typinferenzregeln für Operatorüberlastungen sich (vielleicht unbeabsichtigt) von denen für integrierte Operatoren unterscheiden? –

+0

Vielleicht kann ich ja für beide Fragen ja sagen. Sie können in Swift keine ternären Operatoren schreiben, auch wenn Sie versuchen, vorhandene '?:' Zu überladen. Und über Typinferenzregeln ist die Tatsache, wie Sie sehen. Und ich meine nicht, dass diese Tatsache ist, wie es sein sollte. In Anbetracht der Anwendungsfälle des Koaleszenzoperators Nil sollte Swift die nicht optionale erste Strategie verwenden, denke ich. – OOPer