2014-09-01 14 views
22

Ho una classe Swift che deve memorizzare una tabella con i propri metodi. Sfortunatamente ciò sta causando un ciclo di riferimento, poiché la sua tabella mantiene i riferimenti a self tramite i metodi memorizzati.Rendersi debole nei metodi in Swift

esempio di codice che perde di seguito:

typealias Callback =()->() 

class CycleInducingClass : NSObject { 
    var myCallbacks = [Callback]() 

    override init() { 
     super.init() 
     myCallbacks.append(myInternalFunction) 
    } 

    func myInternalFunction() { 
     NSLog("lolol: %d", self.myCallbacks.count) 
    } 
} 

L'unica soluzione che ho trovato finora è quello di fare invece questo:

myCallbacks.append({[unowned self] in self.myInternalFunction()}) 

che è abbastanza brutto, e soggetto a errori. Qualche idea migliore? C'è qualche trucco per rendere deboli i riferimenti alle funzioni? per rendere la matrice myCallbacks di tipo myCallbacks : [WeakCallback]() o qualcosa del genere? Per quanto posso dire non riesco nemmeno a costruire una funzione di convenienza weaken come zucchero sintattico sopra il brutto involucro di chiusura sopra.

+0

come aggiungere un parametro a myInternalFunction? che potrebbe essere dichiarato debole ..... non bello però –

+0

Ho anche provato a chiudere tutte le mie funzioni interne, cioè "lasciare myInternalFunction = {[self proprietario] in ...}" che funziona, ma è anche piuttosto brutta. – wxs

risposta

10

Si può certamente creare una funzione per questo. Non so se lo rende drammaticamente migliore, ma è meno soggetto a errori.

func methodPointer<T: AnyObject>(obj: T, method: (T) ->() -> Void) -> (() -> Void) { 
    return { [unowned obj] in method(obj)() } 
} 
... 
myCallbacks.append(methodPointer(self, CycleInducingClass.myInternalFunction)) 

In alternativa, si potrebbe gestire le funzioni di callback come metodo di puntatori:

typealias Callback = (CycleInducingClass) ->() -> Void 
... 
myCallbacks.append(CycleInducingClass.myInternalFunction) 

In tal caso, avresti bisogno di passare self quando li hai chiamato (che può andare bene se non lo fai in realtà fare questo molto):

self.myCallbacks[0](self)() 

Tutto questo si basa sul fatto che un metodo del tipo T con la firma (input) -> (output) è equivalente a una functio n con la firma (T) -> (input) -> (output).

Nel caso in cui sei curioso (ero), la sovrascrittura funziona correttamente in questo caso. Pertanto, se si sottoclasse lo CycleInducingClass e si esegue l'override di myInternalFunction, verrà richiamata la versione corretta. (Che in realtà mi sorprende un po ', e io non sanno ancora esattamente perché funziona, ma lo fa.)

EDIT: Ecco la risposta a questa: https://devforums.apple.com/message/1036509#1036509

+0

Ah, interessante, non mi sono accorto di accedere ai metodi in modo statico e ti darebbero una funzione per associare le istanze della tua classe al metodo di classe. Questo è l'ingrediente che mi mancava. Grazie! – wxs

+2

Cosa succede se la funzione accetta parametri? Come funzionerebbe? – Snowman

1

Robs risposta ha lavorato per me. Ho fatto refactoring che fosse un po 'più OO anche se così ho pensato di condividerle qui nel caso in cui aiuta a qualcun altro:

public protocol WeakCallback{ 
    func invoke() 
} 

public class WeakCallbackInstance<T: AnyObject> : WeakCallback{ 
    private let callback:()->Void 
    private weak var target: T? 

    public init(target: T, action: (T)->()->Void){ 

     self.target = target 
     callback = { [weak target] in 
      action(target!)() 
     } 
    } 

    public func invoke(){ 
     callback() 
    } 
} 

class ExampleUsage{ 

    func usage(){ 
     var callbacks = [WeakCallback]() 

     let one = WeakCallbackInstance(target: DummyCallbackOne(), action:DummyCallbackOne.callbackOne) 
     let two = WeakCallbackInstance(target: DummyCallbackTwo(), action:DummyCallbackTwo.callbackTwo) 

     callbacks.append(one) 
     callbacks.append(two) 
     callbacks.first?.invoke() 
    } 
} 

class DummyCallbackOne{ 
    func callbackOne(){ 
    } 
} 

class DummyCallbackTwo{ 
    func callbackTwo(){ 
    } 
} 
-1

avvolto alcuna funzione param con il blocco

myCallbacks.append({ [unowned self] in self.myInternalFunction() }) 

funzione param avvolto con bloccare

myCallbacks.append({ [unowned self] page in self.reloadData(page: page) }) 
0

a Swift 4 (non sono sicuro se la sintassi è diventato disponibile), è sufficiente fare { [weak self] (params) in } per fare self debole. Fondamentalmente è a [unowned self], quello che Self? è a Self!. Il compilatore richiede anche self?.foo anziché semplicemente self.foo.