2014-09-01 6 views
22

Ich habe eine Swift-Klasse, die eine Tabelle ihrer eigenen Methoden speichern muss. Leider verursacht dies einen Referenzzyklus, da seine Tabelle über die Methoden, die sie speichert, Verweise auf self enthält.Machen Sie selbst schwach in Methoden in Swift

Beispiel undichter Code unten:

typealias Callback =()->() 

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

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

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

Die einzige Lösung, die ich bisher gefunden habe, dies zu tun, anstatt:

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

Das ziemlich hässlich ist, und anfällig für Fehler. Irgendwelche besseren Ideen? Gibt es einen Trick, um die Funktionsreferenzen selbst schwach zu machen? d. h., um das myCallbacks Array vom Typ myCallbacks : [WeakCallback]() oder etwas zu machen? Soweit ich das beurteilen kann, kann ich nicht einmal eine Convenience-Funktion weaken als syntaktischen Zucker über den hässlichen Verschluss-Wrapper oben bauen.

+0

Wie wäre es, einen Parameter zu myInternalFunction hinzuzufügen? das könnte schwach erklärt werden ..... nicht schön, obwohl –

+0

Ich habe auch versucht, nur alle meine internen Funktionen closures, dh 'let myInternalFunction = {[unowned self] in ...}' das funktioniert, ist aber auch eher hässlich. – wxs

Antwort

10

Sie können sicherlich eine Funktion dafür bauen. Ich weiß nicht, ob es das dramatisch besser macht, aber es ist weniger fehleranfällig.

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

Alternativ können Sie Ihre Rückrufe als Methodenzeiger gelänge:

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

In diesem Fall müssten Sie self passieren, wenn man sie genannt (was in Ordnung sein, wenn Sie nicht tun tatsächlich tut viel this):

self.myCallbacks[0](self)() 

All das beruht auf der Tatsache, dass ein Verfahren vom Typ T mit Unterschrift auf einen (input) -> (output) functio entspricht n mit der Signatur (T) -> (input) -> (output).

Falls Sie neugierig sind (ich war), funktioniert das Überschreiben in diesem Fall korrekt. Wenn Sie also CycleInducingClass ableiten und myInternalFunction überschreiben, wird die richtige Version aufgerufen. (Das überrascht mich tatsächlich ein wenig, und ich weiß noch nicht genau wissen, warum es funktioniert, aber es funktioniert.)

EDIT: Hier ist die Antwort darauf: https://devforums.apple.com/message/1036509#1036509

+0

Ah interessant, ich habe nicht erkannt, dass Sie statisch auf Methoden zugreifen, und sie würden Ihnen eine Funktion geben, die Instanzen Ihrer Klasse der Klassenmethode zuordnet. Das ist die Zutat, die ich vermisste. Vielen Dank! – wxs

+2

Was passiert, wenn die Funktion Parameter akzeptiert? Wie würde das funktionieren? – Snowman

1

Robs Antwort für mich gearbeitet. Ich habe Refactoring es ein wenig mehr OO zu sein, obwohl so dachte ich, dass ich es hier für den Fall, teilen würde hilft es jemand anderes:

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

gewickelt keine param Funktion mit Block

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

gewickelte param Funktion mit Block

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

In Swift 4 (ich bin nicht sicher, wenn die Syntax zur Verfügung steht), tut einfach { [weak self] (params) in }self schwach zu machen. Es geht grundsätzlich um [unowned self], was Self? zu Self! ist. Der Compiler benötigt sogar self?.foo anstatt einfach self.foo.