È ansible replicare il valore numerico automatico di Swifts ponticellato su Foundation (NSNumber) per i tipi (U) Int8 / 16/32/64?

Domanda

  • È ansible replicare il valore numerico di Swifts con il bridging su Foundation: s NSNumber tipo di riferimento, ad esempio per i tipi Int32 , UInt32 , Int64 e UInt64 ? In particolare, replicando il bridging di assegnazione automatica descritto di seguito.

Esempio di utilizzo previsto di tale soluzione:

 let foo : Int64 = 42 let bar : NSNumber = foo /* Currently, as expected, error: cannot convert value of type 'Int64' to specified type 'NSNumber */ 

sfondo

Alcuni dei tipi di numeri (valore) nativi di Swift possono essere automaticamente colmati con il tipo NSNumber (riferimento):

Le istanze dei tipi di strutture numeriche Swift, come Int , UInt , Float , Double e Bool , non possono essere rappresentate dal tipo AnyObject , poiché AnyObject rappresenta solo le istanze di un tipo di class. Tuttavia, quando il bridging su Foundation è abilitato, i valori numerici Swift possono essere assegnati a costanti e variabili di tipo AnyObject come istanze a ponte della class NSNumber .

Swift NSNumber automaticamente determinati tipi di numeri nativi, come Int e Float , a NSNumber . Questo bridging ti consente di creare un NSNumber da uno di questi tipi:

 let n = 42 let m: NSNumber = n 

Consente inoltre di passare un valore di tipo Int , ad esempio, a un argomento che si aspetta un NSNumber . …

Tutti i seguenti tipi vengono automaticamente ponticellati su NSNumber:

 - Int - UInt - Float - Double - Bool 

Dall’interoperabilità – Lavorare con i tipi di dati del cocoa – Numeri .

Quindi, perché IntXX a replicare questo per i tipi IntXX / UIntXX ?

In primo luogo: la curiosità, scatenata dal vedere alcune domande recentemente con problemi sottostanti che coprivano la confusione sul perché un tipo di valore Int apparentemente può essere rappresentato da una AnyObject (riferimento), mentre ad esempio Int64 , non può; che è naturalmente spiegato dal ponte coperto sopra. Per citarne alcuni:

  • Perché una Swift Array è compatibile con AnyObject?
  • Can not subscript un valore di tipo ‘[UInt32]’
  • Utilizzo di array generici in rapido

Nessuna delle domande e AnyObject sopra menziona, tuttavia, la possibilità di implementare effettivamente tale bridging automatico su AnyObject ( NSNumber ) dai tipi non a ponte Int64 , UInt16 e così via. Le risposte in questi thread si focalizzano piuttosto (correttamente) sulla spiegazione del motivo per cui AnyObject non può contenere tipi di valore e su come i tipi IntXX / UIntXX non vengono UIntXX a ponte per la conversione automatica ai tipi di base del precedente.

Secondariamente: per le applicazioni in esecuzione su architetture sia a 32 bit che a 64 bit, esistono alcuni casi di utilizzo limitati, utilizzando i tipi di numeri nativi Swift implicitamente convertiti in AnyObject , in alcuni contesti, dove l’utilizzo di Int32 o Int64 sarebbe preferibile rispetto a Int . Un esempio (un po ‘) di questo tipo:

  • Perché questo codice funzione casuale si blocca su un iPhone 5 e 5S.

Sì (è ansible): per conformità al protocollo _ObjectiveCBridgeable

(La seguente risposta è basata sull’uso di Swift 2.2 e XCode 7.3.)

Proprio mentre stavo valutando se postare o semplicemente saltare questa domanda, mi sono imbattuto in swift/stdlib/public/core/BridgeObjectiveC.swift nel codice sorgente Swift, in particolare nel protocollo _ObjectiveCBridgeable . Ho notato brevemente il protocollo in precedenza su Swiftdoc.org , ma nella sua forma attuale (vuota) in quest’ultimo, non ci ho mai pensato molto. Usando i blueprints per _ObjectiveCBridgeable dal sorgente Swift possiamo, tuttavia, consentire rapidamente ad alcuni nativi del tipo personalizzato di conformarsi ad esso.

Prima di procedere, nota che _ObjectiveCBridgeable è un protocollo interno / nascosto ( _UnderScorePreFixedProtocol ), quindi le soluzioni basate su di esso potrebbero interrompersi senza preavviso nelle prossime versioni di Swift.


Abilitazione del bridge Int64 alla class Foundation NSNumber

Ad esempio, estendere Int64 per conformarsi a _ObjectiveCBridgeable e successivamente verificare se questa correzione abbastanza semplice è sufficiente per la conversione del tipo implicito (bridging) da Int64 alla class Foundation NSNumber .

 import Foundation extension Int64: _ObjectiveCBridgeable { public typealias _ObjectiveCType = NSNumber public static func _isBridgedToObjectiveC() -> Bool { return true } public static func _getObjectiveCType() -> Any.Type { return _ObjectiveCType.self } public func _bridgeToObjectiveC() -> _ObjectiveCType { return NSNumber(longLong: self) } public static func _forceBridgeFromObjectiveC(source: _ObjectiveCType, inout result: Int64?) { result = source.longLongValue } public static func _conditionallyBridgeFromObjectiveC(source: _ObjectiveCType, inout result: Int64?) -> Bool { self._forceBridgeFromObjectiveC(source, result: &result) return true } } 

Test:

 /* Test case: scalar */ let fooInt: Int = 42 let fooInt64: Int64 = 42 var fooAnyObj : AnyObject fooAnyObj = fooInt // OK, natively fooAnyObj = fooInt64 // OK! _ObjectiveCBridgeable conformance successful /* Test case: array */ let fooIntArr: [Int] = [42, 23] let fooInt64Arr: [Int64] = [42, 23] var fooAnyObjArr : [AnyObject] fooAnyObjArr = fooIntArr // OK, natively fooAnyObjArr = fooInt64Arr // OK! _ObjectiveCBridgeable conformance successful 

Quindi, la conformità a _ObjectiveCBridgeable è in effetti sufficiente per abilitare il bridging di assegnazione automatica alla class Foundation corrispondente; in questo caso, NSNumber (in Swift, __NSCFNumber ).


Abilitazione di Int8 , UInt8 , Int16 , UInt16 , Int32 , UInt32 , ( Int64 ) e NSNumber che si NSNumber a NSNumber

La conformità di Int64 a _ObjectiveCBridgeable può essere facilmente modificata per coprire tutti i tipi interi nativi di Swift, usando la tabella di conversione NSNumber seguito.

 /* NSNumber initializer: NSNumber native Swift type property -------------------------------- ----------------------------------- init(char: ) .charValue init(unsignedChar: ) .unsignedCharValue init(short: ) .shortValue init(unsignedShort: ) .unsignedShortValue init(int: ) .intValue init(unsignedInt: ) .unsignedIntValue init(longLong: ) .longLongValue init(unsignedLongLong: ) .unsignedLongLongValue */