dan probably offered the simplest answer for your use case.
If you ever need to store a "weak method", though, you can use this:
public struct WeakMethod<Reference: AnyObject, Input, Output> {
public init(
reference: Reference?,
method: @escaping Method
) {
self.reference = reference
self.method = method
}
public weak var reference: Reference?
public var method: Method
}
public extension WeakMethod {
struct ReferenceDeallocatedError: Error { }
typealias Method = (Reference) -> (Input) -> Output
/// - Throws: ReferenceDeallocatedError
func callAsFunction(_ input: Input) throws -> Output {
guard let reference = reference
else { throw ReferenceDeallocatedError() }
return method(reference)(input)
}
// MARK:-
init<Input0, Input1>(
reference: Reference?,
method: @escaping (Reference) -> (Input0, Input1) -> Output
)
where Input == (Input0, Input1) {
self.reference = reference
self.method = { reference in
{ method(reference)($0.0, $0.1) }
}
}
/// - Throws: ReferenceDeallocatedError
func callAsFunction<Input0, Input1>(_ input0: Input0, _ input1: Input1) throws -> Output
where Input == (Input0, Input1) {
try self( (input0, input1) )
}
}
public extension WeakMethod where Input == () {
init(
reference: Reference?,
method: @escaping (Reference) -> () -> Output
) {
self.reference = reference
self.method = { reference in
{ _ in method(reference)() }
}
}
/// - Throws: ReferenceDeallocatedError
func callAsFunction() throws -> Output {
try self( () )
}
}
var boss: Boss? = .init()
let callBack = WeakMethod(reference: boss, method: Boss.callBack)
try callBack()
boss = nil
try callBack() // ReferenceDeallocatedError
I capitalized the "B" because callBack
is a predicate (verb + object), not a noun. I advise against using it in any form, though. "callback" is a term from antiquity that isn't used in Swift.
Note: you can also create your own "methods" using closures, if you have a reason for it.
final class WeakMethodTestCase: XCTestCase {
func test_method_noParameters() throws {
var reference: Reference? = Reference()
let assign1234 = WeakMethod(reference: reference, method: Reference.assign1234)
try assign1234()
XCTAssertEqual(reference?.property, 1234)
reference = nil
XCTAssertThrowsError( try assign1234() ) {
XCTAssert($0 is WeakMethod<Reference, (), Void>.ReferenceDeallocatedError)
}
}
func test_method_2Parameters() throws {
var reference: Reference? = Reference()
let assignSum = WeakMethod(reference: reference, method: Reference.assignSum)
try assignSum(2, 3)
XCTAssertEqual(reference?.property, 5)
reference = nil
XCTAssertThrowsError( try assignSum(2, 3) ) {
XCTAssert($0 is WeakMethod<Reference, (Int, Int), Void>.ReferenceDeallocatedError)
}
}
func test_closure_noParameters() throws {
var reference: Reference? = Reference()
let assign1234 = WeakMethod(reference: reference) {
reference in { reference.property = 1234 }
}
try assign1234()
XCTAssertEqual(reference?.property, 1234)
reference = nil
XCTAssertThrowsError( try assign1234() ) {
XCTAssert($0 is WeakMethod<Reference, (), Void>.ReferenceDeallocatedError)
}
}
func test_closure_1Parameter() throws {
var reference: Reference? = Reference()
let assign = WeakMethod(reference: reference) {
reference in { reference.property = $0 }
}
try assign(1234)
XCTAssertEqual(reference?.property, 1234)
reference = nil
XCTAssertThrowsError( try assign(1234) ) {
XCTAssert($0 is WeakMethod<Reference, Int, Void>.ReferenceDeallocatedError)
}
}
}
private final class Reference {
var property = 1
func assign1234() {
property = 1234
}
func assignSum(int0: Int, int1: Int) {
property = int0 + int1
}
}