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!
Update a user from Apple Dev.Forums pointed out that sleep is not a good function to use and consecutive dispatches might cause race conditions. While those might be reasonable concerns, this is just a sample code, the focus of the question is on passing weak arguments.
Swift doesn't have “weak args”… but that's probably only because Swift (3.0) args are immutable (equivalent to lets) and weak things in Swift need to be both a var and an Optional.
That said, there is indeed a pretty-easy way to accomplish an equivalent to weak args— use a weak var local (which frees up the arg-var to be released). This works because Swift doesn't hang onto vars until the end of the current scope (like C++ so strictly does); but rather it releases vars from the scope after the last usage of them (which makes lldb-ing a PitA sometimes, but whatever).
The following example works consistently in Swift 3.0.2 on Xcode 8.2.1 on macOS 10.11.6:
class Test
{
func runTest() {
var object:NSObject? = NSObject()
myMethod(arg0: object)
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + 1.0,
qos: .userInteractive,
flags: DispatchWorkItemFlags.enforceQoS
){
object = nil
}
}
func myMethod(arg0:AnyObject?) {
weak var arg0Weak = arg0
// `arg0` get “released” at this point. Note: It's essential that you
// don't use `arg0` in the rest of this method; only use `arg0Weak`.
NSLog("\(arg0Weak)"); // Optional(<NSObject: 0x600000000810>)
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + 2.0,
qos: .userInteractive,
flags: DispatchWorkItemFlags.enforceQoS
){
NSLog("\(arg0Weak)"); // nil
}
}
}
Test().runTest()
Note that if you try this in a Playground, the playground will finish execution before the DispatchQueues fire. The simplest way to get the executable to run indefinitely (what I did) is create a new Cocoa application and paste all the code above into the func applicationDidFinishLaunching(_:Notification) { … } (yes, verbatim— Swift allows class definitions nested inside of methods).
In response to the thread-safety-lecturing you've gotten over using dispatch_async & sleep in your example, to prove that weak args are indeed the real deal here's a complete-main.m-source variant of your test that's single-threaded and queue-free:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (void)runTest;
- (void)myMethod:(__weak id)arg0 callback:(void (^)())callback;
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
[[Test new] runTest];
}
return 0;
}
#implementation Test
- (void)runTest {
__block NSObject *object = [NSObject new];
[self myMethod:object callback:^{
object = nil;
}];
}
- (void)myMethod:(__weak id)arg0 callback:(void (^)())callback {
NSLog(#"%#", arg0); // <NSObject: 0x100400bc0>
callback();
NSLog(#"%#", arg0); // (null)
}
#end
No way with language syntax for now.
I think this workaround is closest one for now.
public struct Weak<T> where T: AnyObject {
public weak var object: T?
public init(_ object: T?) {
self.object = object
}
}
func run(_ a: Weak<A>) {
guard let a = a.object else { return }
}
Is there any way to specify that a particular method argument has weak semantics?
That isn't what your Objective-C code example is doing. You're getting accidentally almost-weak semantics and you have undefined behavior (race condition) that real weak references don't have.
myMethod can send a message into la-la-land at any sequence point (the first NSLog statement or second, or even in the middle of NSLog somewhere... even if ARC doesn't elide the retain of arg0 you're still racing the main queue release or worse - retaining a zombie object).
Declaring something as __block just means allocate a slot in the heap environment for the block (because dispatch_async is guaranteed to let the block escape it will promote from a stack-allocated block to a heap block, and one of the storage slots in that heap block environment will be for your __block variable. Under ARC the block will automatically have Block_copy called, perhaps more aptly named Block_copy_to_heap).
This means both executing instances of the block will point to this same memory location.
If it helps, imagine this really silly code which has an obvious race condition. There are 1000 blocks queued concurrently all trying to modify unsafe. We're almost guaranteed to execute the nasty statements inside the if block because our assignment and comparison are not atomic and we're fighting over the same memory location.
static volatile size_t unsafe = 0;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(1000, queue, ^(size_t instance) {
unsafe = instance;
if (unsafe != instance) {
FORMAT_ALL_STORAGE();
SEND_RESIGNATION_EMAIL();
WATCH_THE_WORLD_BURN();
}
});
Your Swift example doesn't have the same problem because the block that doesn't modify the value probably captures (and retains) the object so doesn't see the modification from the other block.
It is up to the creator of a closure to deal with the memory management consequences so you can't create an API contract that enforces no retain cycles in a closure, other than marking something as #noescape in which case Swift won't do any retain/release or other memory management because the block doesn't outlive the current stack frame. That precludes async dispatch for obvious reasons.
If you want to present an API contract that solves this you can have a type adopt a protocol protocol Consumer: class { func consume(thing: Type) } then inside your API keep a weak reference to the Consumer instances.
Another technique is to accept a curried version of an instance function and weakly capture self:
protocol Protocol: class { }
typealias FuncType = () -> Void
var _responders = [FuncType]()
func registerResponder<P: Protocol>(responder: P, usingHandler handler: (P) -> () -> Void) {
_responders.append({ [weak responder] in
guard let responder = responder else { return }
handler(responder)()
})
}
class Inst: Protocol {
func myFunc() {
}
}
let inst = Inst()
registerResponder(inst, usingHandler: Inst.myFunc)
Related
So I read that when running DispatchQueue.main.async { } you don't need to declare [weak self]. However, i joined a new project, and stumbled across this:
public func dispatchOnMain(execute: #escaping () -> Void) {
if Thread.isMainThread {
execute()
return
}
DispatchQueue.main.async(execute: execute)
}
Which is an open global function. From here on, doing something like:
class Foo {
func bar() {
dispatchOnMain {
// Do something
}
}
}
Will this dispatchOnMain { require a weak self? And why / why not?
You're way overthinking this. The need for weak self has nothing to do with what thread you're going to be on, and it has nothing to do with dispatch queues. If you're going to be worrying about weak self every time you see a pair of curly braces, you'll be a nervous wreck. 😃
Weak self has to do with whether there will be a retain cycle. Retain cycles are caused through long term storage of a closure by an object it captures.
Nothing like that is going on here. When you are just saying "perform this action now", regardless of the thread, no retain cycle arises. So no precautions are necessary.
I have the following class, which uses a closure in one of its methods:
class SomeClass {
let someOtherClassInstance: OtherClass
func performAsyncTask() {
DispatchQueue.global(qos: .background).async { [weak self] in
print("\(self?.someOtherClassInstance)")
}
}
}
I'm wondering if I can also rewrite performAsyncTask as:
func performAsyncTask() {
let instance = self.someOtherClassInstance
DispatchQueue.global(qos: .background).async {
print("\(instance)")
}
}
The main goal is that I can avoid making self weak in the capture list - or rather so that I don't have to access self at all. There seems to be no reference to self in the second version, but is there a possibility that there will be an error when I try to access instance?
That's fine (assuming that self.someOtherClassInstance has no
back references to the SomeClass instance). You can achieve the
same with a capture list:
func performAsyncTask() {
DispatchQueue.global(qos: .background).async {
[instance = self.someOtherClassInstance] in
print("\(instance)")
}
}
The closure captures a strong reference to the
OtherClass instance which is held until it has been executed,
but no reference to self.
Note that the closure accesses instance regardless of whether
the SomeClass instance still exists or not, so the behavior is
slightly different from what your first method does.
I have a class object in the controller, and then I have a closure in this object.
I assign a function of the controller to the object's closure, and then the page does not deinit.
How can I solve this problem?
import UIKit
class SecondViewController: UIViewController {
let test = TestObject()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.test.select = self.selectButton(index:)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.test.doSomethine()
}
func selectButton(index:Int){
print(index)
}
deinit {
print("deinit")
}
}
import UIKit
typealias selectBtnBlock = (_ index:Int)->()
class TestObject: NSObject {
var select:selectBtnBlock?
func doSomethine(){
self.select!(1)
}
}
This is because your test object's select closure strongly captures your SecondViewController when you do the following:
self.test.select = self.selectButton(index:)
I recommend you do some reading about weak and strong types via Apple's Swift language reference. The "interesting phenomenon" you encountered is called a strong reference cycle.
Essentially, since Swift uses ARC as its memory management model, any object that is referenced by at least one other object else will be kept alive, and its memory not deallocated.
In your case, test has captured its parent SecondViewContoller via the line I mentioned. What that means is you have a situation like the following:
SecondViewController -> (owns) test // since its a member of the class
test -> (strongly captures) SecondViewController // via the assignment
This causes a strong reference cycle between the two, and does not allow ARC to deallocate either.
When it (ARC) tries to free up test, is knows that SecondViewController references it, so it can be freed only if the parent is also freed. When it tries to deallocate SecondViewController, ARC knows that this object is referenced by test.select closure.
Since both have a reference count greater than one, neither will get deallocated.
One way to solve your issue is to write:
self.test.select = {
[weak self] // weakly capture self, this prevents a ref cycle
(i:Int)->() in // a closure that accepts an Int
guard let s = self else { return } // check if self is not nil
s.selectButton(index: i) // finally invoke the required method
}
Another way, similar intent:
self.test.select = { [weak self] i in
self?.selectButton(index: i)
}
The weak keyword in this context is used to tell the Swift compiler that I do not want to keep a strong reference to what I am capturing (self in this case).
Up till now, I've been naïvely using Swift without really caring about the memory management. But I'm implementing a capture list, and I guess it sort of makes sense.
My question is - why wouldn't self be automatically made unowned to avoid retain cycles? Is there a situation in which you'd explicitly need self to be owned that couldn't be resolved by saving some of its data elsewhere?
Give you a simple example
This is a class that I need to use self,not unowned self
If I use self here
class Test{
func log(){
println("log");
}
func FunctionHaveBlock(){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
() -> Void in
sleep(4)
self.log()
})
}
deinit{
println("Deinit")
}
}
Then call
var test:Test? = Test()
test!.FunctionHaveBlock();
test = nil;
The code will executed well,after 4 seconds,it will log
log
Deinit
But if I changed to unowned self,
class Test{
func log(){
println("log");
}
func FunctionHaveBlock(){
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
[unowned self]
() -> Void in
sleep(4)
self.log()
})
}
deinit{
println("Deinit")
}
}
Then call
var test:Test? = Test()
test!.FunctionHaveBlock();
test = nil;
It will log
Deinit
then After 4 seconds,the app crashed.Because,the object is dealloced.
So,if you need to retain the object,you do not use unowned self
Swift Closure will have a strong reference cycle when it refers to self like this example:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
}
In the previous example, I created a strong reference cycle so I have to fix it with:
class Test {
var name = "Hello"
func doSomething() {
{[unowned self] () -> Void in
self.name = "otherName"
}()
}
}
Question: If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
Neither. In most cases, just refer to self normally and do nothing with its memory management. You only have to worry about memory management if there is a danger of a retain cycle, and unless you store the closure somewhere, such as a property of self, there is no such danger.
You can easily prove this by adding a deinit implementation:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
deinit {
println("bye")
}
}
Now make a Test instance and immediately release it:
func testTest () {
let t = Test()
}
You see "bye" in the console, proving that the instance was released in good order. There was never any kind of "strong reference cycle" in this code. Your concerns are groundless.
[By the way, you are using the word "closure" wrong. Every Swift function is a closure. If there were a retain cycle issue merely because of using the word self in a closure, every Swift function would be subject to this issue - and clearly that is not the case. The place where weak and unowned self comes into play is in an anonymous function - and only, as I said before, if that anonymous function is itself also retained by self.]