2014-09-01 14 views
22

Mam klasę Swift, która musi przechowywać tabelę własnych metod. Niestety powoduje to cykl odniesienia, ponieważ jego tabela zachowuje odwołania do self za pomocą metod, które przechowuje.Słabość w metodach w Swift

Przykład kodu nieszczelny poniżej:

typealias Callback =()->() 

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

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

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

Jedynym rozwiązaniem znalazłem tak daleko jest zamiast tego zrobić:

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

To dość brzydkie, i podatne na błędy. Jakieś lepsze pomysły? Czy istnieje jakaś sztuczka, która sprawia, że ​​same odwołania do funkcji są słabe? tj. utworzyć tablicę myCallbacks typu myCallbacks : [WeakCallback]() lub coś podobnego? O ile mogę powiedzieć, nie mogę nawet zbudować funkcji wygody weaken jako cukru syntaktycznego nad brzydkim opakowaniem zamknięcia powyżej.

+0

Co powiesz na dodanie parametru do mojejInternalFunkcji? które można by uznać za słabe ..... niezbyt ładne. –

+0

Próbowałem też po prostu zamknąć wszystkie wewnętrzne funkcje, tj. 'let myInternalFunction = {[unowned self] in ...}' działa, ale jest również raczej brzydki. – wxs

Odpowiedz

10

Z pewnością można zbudować dla tego funkcję. Nie wiem, czy to znacznie poprawia, ale jest mniej podatny na błędy.

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

Alternatywnie, można zarządzać zwrotnych jako metoda wskaźników:

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

W takim przypadku trzeba by przejść self kiedy zadzwoniłem do nich (który może być w porządku, jeśli nie rzeczywiście to dużo) zrobić:

self.myCallbacks[0](self)() 

Wszystko to opiera się na fakcie, że metoda typu T z podpisem (input) -> (output) jest równoznaczne z funkcjono nz podpisem (T) -> (input) -> (output).

W przypadku, gdy jesteś ciekawy (byłem), nadpisywanie działa poprawnie w tym przypadku. Więc jeśli podklasa CycleInducingClass i zastąpi myInternalFunction, zostanie wywołana właściwa wersja. (To faktycznie dziwi mnie trochę, a ja nie wiem jeszcze dokładnie, dlaczego to działa, ale to robi.)

EDIT: Oto odpowiedź na to: https://devforums.apple.com/message/1036509#1036509

+0

Ah ciekawe, nie zdawałem sobie sprawy, że masz dostęp do metod statycznych i dostarczyłyby one instancje odwzorowujące funkcje twojej klasy na metodę klasy. To jest składnik, którego mi brakowało. Dzięki! – wxs

+2

Co, jeśli funkcja przyjmuje parametry? Jak by to działało? – Snowman

1

Robs odpowiedź pracował dla mnie. I nie byłaby to być trochę bardziej OO choć tak myślałem, że chciałbym to zrobić tutaj w przypadku pomaga ktoś inny:

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

owinięty żadnej funkcji param z bloku

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

owinięty funkcji param z blokować

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

w Swift 4 (nie jestem pewien, kiedy składnia stały się dostępne), po prostu zrobić { [weak self] (params) in } aby self słaby. Zasadniczo jest to [unowned self], czyli Self? na Self!. Kompilator wymaga nawet self?.foo zamiast po prostu self.foo.

Powiązane problemy