Подтвердить что ты не робот

Сделать себя слабым в методах в Swift

У меня есть класс Swift, который должен хранить таблицу собственных методов. К сожалению, это вызывает опорный цикл, потому что его таблица сохраняет ссылки на self с помощью методов, которые он хранит.

Пример кода утечки ниже:

typealias Callback = ()->()

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

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

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

Единственное решение, которое я нашел до сих пор, заключается в том, чтобы сделать это:

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

Это довольно уродливо и подвержено ошибкам. Любые лучшие идеи? Есть ли какой-то трюк для того, чтобы сами ссылки на функции были слабыми? т.е. создать массив myCallbacks типа myCallbacks : [WeakCallback]() или что-то еще? Насколько я могу судить, я даже не могу создать удобную функцию weaken в качестве синтаксического сахара над уродливой закрывающей упаковкой выше.

4b9b3361

Ответ 1

Вы можете создать для этого функцию. Я не знаю, улучшилось ли это, но оно менее подвержено ошибкам.

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

В качестве альтернативы вы можете управлять своими обратными вызовами как указатели на методы:

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

В этом случае вам нужно передать self, когда вы их вызвали (что может быть хорошо, если вы на самом деле этого не делаете):

self.myCallbacks[0](self)()

Все это основано на том, что метод типа T с сигнатурой (input) -> (output) эквивалентен функции с сигнатурой (T) -> (input) -> (output).

В случае, если вам любопытно (я был), в этом случае правильное выполнение корректно работает. Поэтому, если вы подклассом CycleInducingClass и переопределите myInternalFunction, вызывается правильная версия. (Это меня немного удивляет, и я пока не знаю, почему он работает, но он это делает.)

EDIT: вот ответ на этот вопрос: https://devforums.apple.com/message/1036509#1036509

Ответ 2

Ответ Роба сработал у меня. Я сделал рефакторинг, чтобы быть немного более OO, хотя я думал, что поделюсь им здесь, если это поможет кому-то другому:

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(){
    }
}

Ответ 3

В Swift 4 (я не уверен, когда синтаксис стал доступен), просто сделайте { [weak self] (params) in } чтобы сделать self слабым. Это, в основном, [unowned self], что Self? это для Self! , Компилятор даже требует self?.foo вместо просто self.foo.

Ответ 4

не завершена никакая функция param с блоком

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

завернутая функция param с блоком

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