Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 878 Vote(s) - 3.55 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Make self weak in methods in Swift

#1
I have a Swift class that needs to store a table of its own methods. Unfortunately this is causing a reference cycle, because its table retains references to `self` via the methods it stores.

Example leaky code below:

typealias Callback = ()->()

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

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

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

The only solution I've found so far is to instead do this:

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

That's pretty ugly, and prone to error. Any better ideas? Is there some trick for making the function references themselves be weak? i.e. to make the `myCallbacks` array of type `myCallbacks : [WeakCallback]()` or something? As far as I can tell I can't even build a convenience function `weaken` as syntactic sugar over the ugly closure wrapper above.

Reply

#2
With Swift 5.2, `callAsFunction` allows for nice syntax for this, until argument labels come into play.

```swift
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)
}
}

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

```swift
final class WeakMethodTestCase: XCTestCase {
func test_method() 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_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
}
}
```

Unfortunately, as you keep adding parameters, you'll need to keep adding initializer+method pairs, like so:

```swift
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) )
}
```
Reply

#3
In Swift 4 (I am not sure when the syntax became available), simply do `{ [weak self] (params) in }` to make `self` weak. It basically is to `[unowned self]`, what `Self?` is to `Self!`. The compiler even requires `self?.foo` instead of simply `self.foo`.
Reply

#4
wrapped no param function with block

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

wrapped param function with block

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

Reply

#5
Robs answer worked for me. I did refactor it to be a little more OO though so I thought I would share it here in case it helps someone else:


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

#6
You can certainly build a function for this. I don't know if it makes it dramatically better, but it is less error-prone.

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

Alternately, you could manage your callbacks as method pointers:

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

In that case, you'd need to pass `self` when you called them (which may be fine if you don't actually do this a lot):

self.myCallbacks[0](self)()

All of this is based on the fact that a method on type `T` with signature `(input) -> (output)` is equivalent to a function with the signature `(T) -> (input) -> (output)`.

In case you're curious (I was), overriding works correctly in this case. So if you subclass `CycleInducingClass` and override `myInternalFunction`, the correct version will be called. (That actually surprises me a little, and I don't yet know exactly why it works, but it does.)

EDIT: Here's the answer to that:

[To see links please register here]

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through