Come posso fare un riferimento di protocollo debole in Swift 'puro' (senza @objc)


561

weaki riferimenti non sembrano funzionare in Swift a meno che non protocolvenga dichiarato come @objc, cosa che non voglio in un'app Swift pura.

Questo codice genera un errore di compilazione ( weaknon può essere applicato a un tipo non di classe MyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

Ho bisogno di aggiungere il prefisso al protocollo @objc, quindi funziona.

Domanda: Qual è il modo "puro" Swift per realizzare un weak delegate?


Risposte:


1039

È necessario dichiarare il tipo di protocollo come AnyObject.

protocol ProtocolNameDelegate: AnyObject {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

Usando AnyObjectte dici che solo le classi possono conformarsi a questo protocollo, mentre le strutture o gli enum non possono.


25
Il mio problema con questa soluzione è che la chiamata al delegato provoca un arresto anomalo: EXC_BAD_ACCESS (come notato da altri utenti). Questo sembra essere un bug. L'unica soluzione che ho trovato è utilizzare @objc ed eliminare tutti i tipi di dati Swift dal protocollo.
Jim T

12
Qual è il modo corretto di fare delegati deboli ora in Swift? La documentazione di Apple non mostra o dichiara debole il delegato nel suo codice di esempio: developer.apple.com/library/ios/documentation/swift/conceptual/…
C0D3

2
Questo non è sempre sicuro - ricorda che devi solo indebolire il delegato se contiene anche un riferimento al delegatore e devi interrompere quel forte ciclo di riferimento. Se il delegato non ha alcun riferimento al delegato, il delegato può uscire dall'ambito (perché è debole) e si verificheranno arresti anomali e altri problemi: / qualcosa da tenere a mente.
Trev14,

5
A proposito: penso che il "nuovo stile" (Swift 5) sia da fare protocol ProtocolNameDelegate: AnyObject, ma non importa.
hnh

1
Dovrebbe essere AnyObjectpoiché classsarà deprecato ad un certo punto.
José,

283

Risposta supplementare

Ero sempre confuso sul fatto che i delegati dovessero essere deboli o meno. Recentemente ho imparato di più sui delegati e su quando usare riferimenti deboli, quindi vorrei aggiungere alcuni punti supplementari qui per il bene dei futuri spettatori.

  • Lo scopo dell'uso della weakparola chiave è di evitare cicli di riferimento forti (cicli di mantenimento). Cicli di riferimento forti si verificano quando due istanze di classe hanno riferimenti forti tra loro. I loro conteggi di riferimento non vanno mai a zero, quindi non vengono mai deallocati.

  • È necessario utilizzare solo weakse il delegato è una classe. Le strutture e gli enumeratori rapidi sono tipi di valore (i loro valori vengono copiati quando viene creata una nuova istanza), non tipi di riferimento, quindi non creano cicli di riferimento forti .

  • weaki riferimenti sono sempre opzionali (altrimenti si utilizzerebbero unowned) e usano sempre var(non let) in modo che sia possibile impostare l'opzione nilquando viene deallocata.

  • Una classe genitore dovrebbe naturalmente avere un forte riferimento alle sue classi figlio e quindi non usare la weakparola chiave. Quando un bambino vuole un riferimento al suo genitore, tuttavia, dovrebbe renderlo un riferimento debole usando la weakparola chiave.

  • weakdovrebbe essere usato quando vuoi un riferimento a una classe che non possiedi, non solo per un bambino che fa riferimento al suo genitore. Quando due classi non gerarchiche devono fare riferimento l'una all'altra, scegline una per essere debole. Quello che scegli dipende dalla situazione. Vedi le risposte a questa domanda per ulteriori informazioni al riguardo.

  • Come regola generale, i delegati devono essere contrassegnati comeweak perché la maggior parte dei delegati fa riferimento a classi che non possiedono. Questo è sicuramente vero quando un bambino utilizza un delegato per comunicare con un genitore. L'uso di un riferimento debole per il delegato è ciò che la documentazione raccomanda. (Ma vedi anche questo .)

  • I protocolli possono essere utilizzati sia per i tipi di riferimento (classi) che per i tipi di valore (strutture, enumerazioni). Pertanto, nel caso probabile che sia necessario rendere debole un delegato, è necessario renderlo un protocollo solo oggetto. Il modo per farlo è quello di aggiungere AnyObjectall'elenco di eredità del protocollo. (In passato lo hai fatto usando la classparola chiave, ma AnyObjectora è preferito .)

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }

Ulteriore studio

Leggere i seguenti articoli mi ha aiutato a capirlo molto meglio. Discutono anche questioni correlate come la unownedparola chiave e i forti cicli di riferimento che si verificano con le chiusure.

Relazionato


5
Tutto ciò è bello e interessante, ma non è in realtà correlato alla mia domanda originale - che non riguarda né il debole / ARC stesso né il motivo per cui i delegati sono generalmente deboli. Sappiamo già tutto questo e ci siamo solo chiesti come si possa dichiarare un riferimento di protocollo debole (risposto perfettamente a @flainez).
hn

30
Hai ragione. In realtà ho avuto la tua stessa domanda prima, ma mi mancavano molte di queste informazioni di base. Ho fatto la lettura sopra e ho preso le note supplementari per aiutarmi a capire tutte le questioni relative alla tua domanda. Ora penso di poter applicare la tua risposta accettata e sapere perché lo sto facendo. Spero che possa aiutare anche i futuri spettatori.
Suragch,

5
Ma posso avere un protocollo debole che NON dipende dal tipo? Un protocollo da solo non importa quale oggetto sia conforme a se stesso. Quindi sia una classe, sia una struttura possono conformarsi ad essa. È possibile avere ancora il vantaggio di essere entrambi in grado di conformarsi ad esso, ma solo i tipi di classe conformi sono deboli?
FlowUI. SimpleUITesting.com il

> poiché la maggior parte dei delegati fa riferimento a classi che non possiedono, riscrivo come segue: la maggior parte dei delegati. Altrimenti l'oggetto non di proprietà diventa proprietario
Victor Jalencas l'

36

AnyObject è il modo ufficiale di utilizzare un riferimento debole in Swift.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

Da Apple:

Per evitare cicli di riferimento forti, i delegati devono essere dichiarati come riferimenti deboli. Per ulteriori informazioni sui riferimenti deboli, vedere Cicli di riferimento forti tra istanze di classe. Contrassegnare il protocollo come solo di classe in seguito consentirà di dichiarare che il delegato deve utilizzare un riferimento debole. Contrassegni un protocollo come solo di classe ereditando da AnyObject , come discusso nei protocolli di sola classe.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID276


7
Interessante. È classdeprecato in Swift 4.1?
hnh

@hnh Puoi ancora fare uno "pseudo-protocollo" rendendolo una classe, ma protocollo: AnyObject fa esattamente quello che l'OP sta chiedendo con meno effetti collaterali rispetto a renderlo una classe. (non è ancora possibile utilizzare un protocollo di questo tipo con tipi di valore, ma dichiararlo una classe non risolverà neanche quello)
Arru,

8

Aggiornamento: sembra che il manuale sia stato aggiornato e che l'esempio a cui mi riferivo sia stato rimosso. Vedi la modifica alla risposta di @ flainez sopra.

Originale: usare @objc è il modo giusto di farlo anche se non stai interagendo con Obj-C. Assicura che il protocollo sia applicato a una classe e non a un enum o a una struttura. Vedere "Verifica della conformità del protocollo" nel manuale.


Come già detto, l'IMO non è una risposta alla domanda. Un semplice programma Swift dovrebbe essere in grado di resistere da solo senza essere legato all'NSismo (questo potrebbe implicare che non si utilizzi più il delegato ma un altro costrutto di progettazione). Il mio puro Swift MyClass in realtà non importa se la destinazione è una struttura o un oggetto, né ho bisogno di opzioni. Forse riescono a risolverlo più tardi, dopo tutto è una nuova lingua. Forse qualcosa come "protocollo di classe XYZ" se è richiesta la semantica di riferimento?
hnh

4
Penso che valga anche la pena notare che \ @objc ha effetti collaterali aggiuntivi: il suggerimento NSObjectProtocol di @eXhausted è leggermente migliore. Con \ @objc - se il delegato della classe accetta un argomento oggetto, come 'handleResult (r: MySwiftResultClass)', MySwiftResultClass ora deve ereditare da NSObject! E presumibilmente non è più nemmeno uno spazio dei nomi, ecc. In breve: \ @objc è una funzionalità di bridging, non una lingua.
hnh

Penso che abbiano risolto questo. Ora scrivi: protocollo MyClassDelegate: class {}
user3675131

Dov'è la documentazione su questo? O sono cieco o sto facendo qualcosa di sbagliato, perché non riesco a trovare alcuna informazione al riguardo ... O_O
BastiBen,

Non sono sicuro che risponda o meno alla domanda del PO, ma questo è utile soprattutto se stai interagendo con Objc-C;)
Dan Rosenstark,

-1

il protocollo deve essere sottoclasse di AnyObject, classe

esempio riportato di seguito

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }

-9

Apple utilizza "NSObjectProtocol" invece di "class".

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

Questo funziona anche per me e ha rimosso gli errori che stavo vedendo quando cercavo di implementare il mio modello di delegato.


5
Non pertinente per la domanda, questa domanda riguarda la creazione di una classe Swift pura (in particolare nessun oggetto NSO) che supporti un oggetto delegato. Non si tratta di implementare protocolli Objective-C, che è quello che stai facendo. Quest'ultimo richiede @objc aka NSObjectProtocol.
hnh

OK, ma non essere raccomandato.
DawnSong,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.