Weak method argument

Hi!


I was trying to google this for quite a while, but apparently I'm failing to find the right keywords.

Is there any way to specify that a particular method argument has weak semantics?


To elaborate, this is an Objective-C sample code that works as expected:

- (void)runTest {
    __block NSObject *object = [NSObject new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self myMethod:object];
    });
    // to make sure it happens after `myMethod:` call
    dispatch_async(dispatch_get_main_queue(), ^{
        object = nil;
    });
}
- (void)myMethod:(__weak id)arg0 {
    NSLog(@"%@", arg0); // <NSObject: 0x7fb0bdb1eaa0>
    sleep(1);
    NSLog(@"%@", arg0); // nil
}

This is the Swift version, that doesn't

public func runTest() {
    var object: NSObject? = NSObject()
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        self.myMethod(object)
    }
    dispatch_async(dispatch_get_main_queue()) {
        object = nil
    }
}
private func myMethod(arg0: AnyObject?) {
    println("\(arg0)") //Optional(<NSObject: 0x7fc778f26cf0>)
    sleep(1)
    println("\(arg0)") //Optional(<NSObject: 0x7fc778f26cf0>)
}

Am I correct in ym assumption that there is no way for the

arg0
to become nil between the method calls in Swift version?

Thank you!

I don't know about the Swift question but I know this


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
        self.myMethod(object) 
    } 
    dispatch_async(dispatch_get_main_queue()) { 
        object = nil 
    }


is a race condition in either Obj-C or Swift. Since they're being dispatched to different queues you have no way of knowing which block will execute first. If your Obj-C example happens to work a particular way when you tried it, that's just luck.

As far as I know the weak attribute can't be applied to a method argument.


But you can specify that a closure captures a weak reference, so the following works similarly to your obj-c version:

import XCPlayground
XCPSetExecutionShouldContinueIndefinitely()


public func runTest() {
    var object: NSObject? = NSObject()
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
    {[weak object] in
        print("1st \(object)") //1st Optional(<NSObject: 0x7fc778f26cf0>)
        sleep(1)
        print("2nd \(object)") //2nd nil
    }
    dispatch_async(dispatch_get_main_queue()) {
        object = nil
    }
}

runTest()

you might be right but it is a rough implementation, you can nest second dispatch_async to avoid it.

while this might work, it doesn't entirely solve my problem: I'm looking for solution on the method side and not the consumer side.

Until there is a way to mark a function parameter so that it is a weak reference and not a pointer copy, if you need to pass a weak reference you will need to do something like wrap the weak reference or maybe pass a pointer to a variable holding the weak reference.


Wrapping it does work similarly to your obj-C code and the closure with a weak reference.


import XCPlayground
XCPSetExecutionShouldContinueIndefinitely()


class Wrapper: NSObject
{   weak var value: AnyObject?
    init(_ v: AnyObject?) {value = v}
}


public func runTest()
{
    var object: NSObject? = NSObject()
    let wrapped = Wrapper(object)
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0))
    {
        myMethod(wrapped)
    }
    dispatch_async(dispatch_get_main_queue()) {
        object = nil
    }
}


private func myMethod(object: Wrapper)
{
    print("1st \(object.value)") //1st Optional(<NSObject: 0x7fc778f26cf0>)
    sleep(1)
    print("2nd \(object.value)") //2nd nil
}




runTest()

The real problem is that 'sleep' is an inappropriate library function to use with GCD. There's no API contract about what it does, in GCD terms. I have no idea what you're really trying to do here, but I think you can get roughly the equivalent of your original Obj-C method like this:


private func myMethod(arg0: AnyObject?) {
    println("\(arg0)")
    dispatch_after (dispatch_time (DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
         println("\(arg0)")
}


That is to say, use GCD to dispatch the second print in a separate block a second later.

Thanks for the reply. This is a sample code. Please ignore sleep, the implementation of the delay is not that important.

yeah, I remember implementing something similar in Objective-C. Didn't like it back then 🙂

I guess I should wait for the language to support it natively

OK, but the principle is the same. Schedule the second print (or whatever it you want to do after the first part has had a chance to run) by scheduling a new block asynchronously.


>>I guess I should wait for the language to support it natively


Swift 2 already does. The following code in a playground looks like it does what you want to do (note that a parameter can't be weak, only a variable declaration). See the section on Automatic Reference Counting in the latest Swift book.


public func runTest() {
  var object: NSObject? = NSObject()
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
  myMethod(object)
  }
  dispatch_async(dispatch_get_main_queue()) {
  object = nil
  }
}
private func myMethod(arg0: AnyObject?) {
  weak var arg1 = arg0
  print("\(arg1)")
  sleep(1)
  print("\(arg1)")
}

I tried it just now – it doesn't release the object for me.

And, logically, it shouldn't, the argument reference is strong, so local weak variable can not "shorten" the lifecycle of the argument.

It seemed to work for me in a playground (I got different "print" results more like your Obj-C example), but maybe the timing is different in an actual app.


What are you actually trying to do? If we knew then perhaps we can suggest a construct that actually achieves it.


Note that in your original Swift code, this:


    dispatch_async(dispatch_get_main_queue()) { 
        object = nil 
    }


does basically nothing, since 'object' is going to be released by the containing scope immediately afterwards. All you're doing is delaying the release until whatever backlog the main queue has at that moment (probably none).


If you're just trying to preserve the lifetime of 'object' until the first step of 'myMethod' (the first print) has finished execution, you should be able to do it by putting the last step (the second print) in a separate closure that doesn't capture the parameter, as I showed earlier.


The other thing to keep in mind is that you are (effectively) tried to reason about retain counts in the logic of this code fragment, and such reasoning is almost always wrong. If you're trying to implement a "object deallocation is resource freeing" scheme, it won't work in general.

I'm trying to achieve behaviour of the objective-c code, where function accepts a weak reference and, therefore does not extend lifecycle of the object.

The sample code is intended to simulate explicit deallocation of the object on the other thread, while function is still executing to check if function is holding strong reference to the argument or not (which from my trials it does)

As for the retai count reasoning. This is not really what I intended to do, all I want to do is to make sure I'm not extending lifecycle of the object by executing my function that migth take a while (sleep), which appearantly I do.


Oh and you are right about object becoming nil at the end of the scope, my bad

Weak method argument
 
 
Q