Il metodo non – ‘@ objc’ non soddisfa il requisito facoltativo del protocollo ‘@objc’

Panoramica:

  • Ho un protocollo P1 che fornisce un’implementazione predefinita di una delle funzioni opzionali Objective-C.
  • Quando fornisco un’implementazione predefinita della funzione opzionale, c’è un avviso

Avvertimento del compilatore:

Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate' 

Versione:

  • Swift: 3
  • Xcode: 8 (versione pubblica)

Tentativi fatti:

  • @objc provato ad aggiungere @objc ma non aiuta

Domanda:

  • Come ho risolto questo?
  • C’è un lavoro in giro?

Codice:

 @objc protocol P1 : UIAdaptivePresentationControllerDelegate { } extension P1 where Self : UIViewController { func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? { return UIViewController() } } class A : UIViewController, P1 { } 

Mentre penso di poter rispondere alla tua domanda, non è una risposta che ti piacerà.

TL; DR: @objc funzioni @objc potrebbero non essere attualmente nelle estensioni del protocollo. Potresti creare una class base, anche se questa non è una soluzione ideale.

Estensioni di protocollo e Objective-C

Innanzitutto, questa domanda / risposta (il metodo Swift può essere definito su estensioni sui protocolli accessibili in Objective-c ) sembra suggerire che a causa del modo in cui le estensioni del protocollo vengono inviate sotto la cappa, i metodi dichiarati nelle estensioni del protocollo non sono visibili a objc_msgSend() funzione, e quindi non sono visibili al codice Objective-C. Dato che il metodo che stai cercando di definire nella tua estensione deve essere visibile a Objective-C (quindi UIKit può usarlo), ti urla per non aver incluso @objc , ma una volta che lo includi, ti urla perché @objc non è consentito nelle estensioni del protocollo. Questo è probabilmente perché 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 aggiunti @objc stati @objc “@objc può essere utilizzato solo con membri di classi, protocolli @objc e estensioni concrete delle classi.” Questa non è una class; un’estensione di un protocollo @objc non è la stessa della definizione del protocollo stesso (cioè nei requisiti), e la parola “concreto” suggerisce che un’estensione di protocollo non conta come un’estensione di class concreta.

Soluzione

Sfortunatamente, questo ti impedisce completamente di utilizzare le estensioni del protocollo quando le implementazioni predefinite devono essere visibili ai framework Objective-C. All’inizio pensavo che forse @objc non fosse consentito nella tua estensione di protocollo perché il compilatore Swift non poteva garantire che i tipi conformi sarebbero classi (anche se hai specificatamente specificato UIViewController ). Così ho messo un requisito di class su P1 . Questo non ha funzionato.

Forse l’unica soluzione è semplicemente usare una class base invece di un protocollo qui, ma questo non è ovviamente del tutto ideale perché una class può avere solo una singola class base ma è conforms a più protocolli.

Se si sceglie di seguire questa rotta, si prega di prendere in considerazione questa domanda ( Swift 3 ObjC Protocollo facoltativo Metodo non chiamato in sottoclass ). Sembra che un altro problema corrente in Swift 3 sia che le sottoclassi non ereditano automaticamente le implementazioni del requisito del protocollo opzionale della loro superclass. La risposta a queste domande usa uno speciale adattamento di @objc per @objc .

Segnalazione del problema

Penso che questo sia già stato discusso tra coloro che lavorano sui progetti open source di Swift, ma si può essere sicuri che siano a conoscenza dell’uso di Bug Reporter di Apple , che probabilmente finirà con lo Swift Core Team, o il reporter di Swift . Ognuno di questi potrebbe trovare il tuo bug troppo ampio o già noto, comunque. Il team di Swift potrebbe anche considerare ciò che stai cercando come una nuova lingua, nel qual caso dovresti prima controllare le mailing list .

Aggiornare

A dicembre 2016, questo problema è stato segnalato alla comunità Swift. Il problema è ancora contrassegnato come aperto con priorità media, ma è stato aggiunto il seguente commento:

Questo è inteso. Non è ansible aggiungere l’implementazione del metodo ad ogni utente, poiché l’estensione potrebbe essere aggiunta dopo la conformità al protocollo. Suppongo che potremmo consentirlo se l’estensione si trova 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.

Aggiornamento 2

A febbraio 2017, questo problema è stato ufficialmente chiuso come “Will not Do” da uno dei membri del team Swift Core con il seguente messaggio:

Questo è intenzionale: le estensioni del protocollo non possono introdurre i punti di ingresso @objc a causa delle limitazioni del runtime Objective-C. Se si desidera aggiungere i punti di ingresso @objc su NSObject, estendere NSObject.

Estendere NSObject o anche UIViewController non realizzerà esattamente ciò che si desidera, ma sfortunatamente non sembra ansible.

Nel (molto) futuro a lungo termine, potremmo essere in grado di eliminare completamente la dipendenza @objc metodi @objc , ma probabilmente non arriverà molto presto dato che i framework Cocoa non sono attualmente scritti in Swift (e non possono esserlo fino a quando non ha una stabile ABI).