Сделать себя слабым в методах в 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
в качестве синтаксического сахара над уродливой закрывающей упаковкой выше.
Ответы
Ответ 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) })