Anche se penso di poter rispondere alla tua domanda, non è una risposta che ti piacerà.
TL; DR: le @objc
funzioni potrebbero non essere attualmente nelle estensioni di protocollo. Potresti invece creare una classe base, anche se non è una soluzione ideale.
Estensioni del protocollo e Objective-C
In primo luogo, questa domanda / risposta ( Metodo Can Swift definito su estensioni su protocolli accessibili in Objective-c ) sembra suggerire che a causa del modo in cui le estensioni di protocollo vengono inviate sotto il cofano, i metodi dichiarati nelle estensioni di protocollo non sono visibili alla objc_msgSend()
funzione, e pertanto non sono visibili al codice Objective-C. Poiché il metodo che stai cercando di definire nella tua estensione deve essere visibile a Objective-C (quindi UIKit
puoi usarlo), ti urla per non includerlo @objc
, ma una volta che lo includi, ti urla perché @objc
non è consentito estensioni di protocollo. Ciò è probabilmente dovuto al fatto che le estensioni del protocollo non sono attualmente in grado di essere visibili a Objective-C.
Possiamo anche vedere che il messaggio di errore una volta che abbiamo aggiunto @objc
afferma "@objc può essere utilizzato solo con membri di classi, protocolli @objc ed estensioni concrete di classi." Questa non è una classe; un'estensione a un protocollo @objc non è la stessa di essere nella definizione del protocollo stesso (cioè nei requisiti), e la parola "concreto" suggerirebbe che un'estensione del protocollo non conta come un'estensione di classe concreta.
Soluzione
Sfortunatamente, questo ti impedisce quasi completamente di usare le estensioni di protocollo quando le implementazioni predefinite devono essere visibili ai framework Objective-C. In un primo momento, ho pensato che forse @objc
non fosse consentito nell'estensione del protocollo perché Swift Compiler non poteva garantire che i tipi conformi sarebbero classi (anche se specificatamente specificato UIViewController
). Quindi ho posto un class
requisito P1
. Questo non ha funzionato.
Forse l'unica soluzione è usare semplicemente una classe base invece di un protocollo qui, ma questo ovviamente non è del tutto ideale perché una classe può avere solo una singola classe base ma è conforme a più protocolli.
Se scegli di seguire questa strada, ti preghiamo di prendere in considerazione questa domanda ( Swift 3 ObjC Optional Protocol Method Not Called in Subclass ). Sembra che un altro problema attuale in Swift 3 sia che le sottoclassi non ereditano automaticamente le implementazioni dei requisiti di protocollo opzionali della loro superclasse. La risposta a queste domande utilizza un adattamento speciale di @objc
per aggirare il problema.
Segnalazione del problema
Penso che questo sia già stato discusso tra coloro che lavorano ai progetti open source di Swift, ma potresti essere sicuro che ne sono a conoscenza utilizzando Bug Reporter di Apple , che probabilmente alla fine si farebbe strada verso lo Swift Core Team, o il bug reporter di Swift . Tuttavia, entrambi potrebbero trovare il tuo bug troppo ampio o già noto. Il team Swift potrebbe anche considerare ciò che stai cercando come una nuova funzionalità linguistica, nel qual caso dovresti prima controllare le mailing list .
Aggiornare
Nel dicembre 2016, questo problema è stato segnalato alla comunità Swift. Il problema è ancora contrassegnato come aperto con una priorità media, ma è stato aggiunto il seguente commento:
Questo è inteso. Non c'è modo di aggiungere l'implementazione del metodo a ogni adottante, poiché l'estensione potrebbe essere aggiunta dopo la conformità al protocollo. Suppongo che potremmo consentirlo se l'estensione è nello stesso modulo del protocollo, però.
Poiché il tuo protocollo si trova nello stesso modulo della tua estensione, tuttavia, potresti essere in grado di farlo in una versione futura di Swift.
Aggiorna 2
Nel febbraio 2017, questo numero è stato ufficialmente chiuso come "Won't Do" da uno dei membri dello Swift Core Team con il seguente messaggio:
Questo è intenzionale: le estensioni del protocollo non possono introdurre punti di ingresso @objc a causa delle limitazioni del runtime Objective-C. Se desideri aggiungere punti di ingresso @objc a NSObject, estendi NSObject.
L'estensione NSObject
o addirittura UIViewController
non realizzerà esattamente ciò che desideri, ma sfortunatamente non sembra che diventerà possibile.
In un futuro (molto) a lungo termine, potremmo essere in grado di eliminare completamente la dipendenza dai @objc
metodi, ma quel momento probabilmente non arriverà presto poiché i framework Cocoa non sono attualmente scritti in Swift (e non possono esserlo finché non avrà un ABI stabile) .
Aggiorna 3
A partire dall'autunno 2019, questo sta diventando meno un problema perché sempre più framework Apple vengono scritti in Swift. Ad esempio, se usi SwiftUI
invece di UIKit
, eludi completamente il problema perché @objc
non sarebbe mai necessario quando ti riferisci a un SwiftUI
metodo.
I framework Apple scritti in Swift includono:
- SwiftUI
- RealityKit
- Combina
- CryptoKit
Ci si aspetterebbe che questo modello continui nel tempo ora che Swift è ufficialmente ABI e modulo stabile a partire da Swift 5.0 e 5.1, rispettivamente.
@objc