Using weak self in dispatch_async function
Assuming, self is an object pointer to a UIViewController
.
Things to consider:
A
UIViewController
is a "UIKit" object. UIKit objects shall not be sent methods on non-main threads, that is - those methods must execute on the main thread only!A block that has been enqueued in a queue - whether this was synchronously or asynchronously - will eventually be executed -- no matter what! Well, unless the program terminates before this can happen.
Captured retainable strong pointers will be retained when the block will be copied (for example, when dispatched asynchronously), and again released when the block will be destroyed (after it finished).
Captured retainable weak pointers will NOT be retained and not released.
In your scenario, where you capture self in the block which is dispatched on the main queue, you don't need to worry that bad things happen.
So, why? And what happens actually?
Since self will be captured in the block which is dispatched asynchronously, self will be implicitly retained, and released again when the block has been finished.
That means, the life-time of self will be extended up until after the block finishes. Notice that your second block is dispatched on the main thread, and it's guaranteed that self is still alive when that block gets executed.
This "extended life" above, might be a desired feature of your program.
If you explicitly don't want to extend the life-time of the UIViewController
object, and instead want the block - when it finally executes - check whether this UIViewController
object does still exist at all, you can use a __weak pointer of self. Note that the block gets eventually executed, no matter whether the UIViewController
is still alive or has been deallocated in the mean time.
You might want the block doing "nothing" if the UIViewController
has been deallocated before the block will get executed:
MyController* __weak weakSelf = self;
dispatch_async(queue, ^{
MyController* strongSelf = weakSelf;
if (strongSelf) {
...
}
else {
// self has been deallocated in the meantime.
}
});
See also: Transitioning to ARC Release Notes
Remember: UIKit
objects shall not be sent methods on non-main threads!
One other subtle error may occur due to the fact that UIKit
objects shall execute methods only on the main thread.
This can be violated, if a block captures a UIKit
object which is dispatched asynchronously, and executes on a non-main thread. It then may happen that the block holds the last strong reference to that UIKit
object. Now, when the block gets eventually executed, the block will be destroyed and the UIKit
object will be released. Since this is the last strong reference to the UIKit
object, it will be deallocated. However, this happens on the thread where the block has been executed - and this is not the main thread! Now, bad things can (and will usually) happen, since the dealloc
method is still a method sent to a UIKit
object.
You can avoid this error, by dispatching a block capturing a strong pointer to that UIKit object, and send it a dummy method:
UIViewController* strongUIKitPointer = ...
dispatch_async(non_main_queue, ^{
... // do something
dispatch(dispatch_get_main_queue(), ^{
[strongUIKitPointer self]; // note: self is a method, too - doing nothing
});
});
In your scenario though, the last strong reference could be only in the block which executes on the main thread. So, you are safe from this subtle error. ;)
Edit:
In your setup, you never have a retain cycle. A retain cycle occurs if a retainable object A strongly references another retainable object B, and object B strongly references A. Note that a "Block" is also a retainable object.
A contrived example with a cyclic reference:
typedef void(^my_completion_block_t)(NSArray* result);
@interface UsersViewController : UIViewController
@property (nonatomic, copy) my_completion_block_t completion;
@property (nonatomic) NSArray* users;
@end
Here, we have a property completion whose value type is a Block. That is, we get an ivar with name _completion
whose type is a Block.
A client may set a completion handler which should be called when a certain operation has finished. Suppose, the operation fetches a list of Users from a remote server. The plan is to set the property users once the operation finished:
The careless approach would accidentally introduce a cyclic reference:
Somewhere in "UsersViewController.m"
self.completion = ^(NSArray* users){
self.users = users;
}
[self fetchUsers]; // start asynchronous task
Here, self holds a strong reference to the ivar _completion
, which is a block. And the block itself captures self, which causes to retain self when the block gets copied when it is dispatched. This is a classic reference cycle.
In order to avoid that cyclic reference, we have a few alternatives:
Using a
__weak
qualified pointer of selfUsersViewController* __weak weakSelf = self;
self.completion = ^(NSArray* users) {
UsersViewController* strongSelf = weakSelf;
if (strongSelf) {
strongSelf.users = users;
}
else {
// the view controller does not exist anymore
}
}
[usersViewController fetchUsers];Using a
__block
qualified pointer of self and eventually setting itnil
in the block when it finishes:UsersViewController* __block blockSelf = self;
self.completion = ^(NSArray* users) {
blockSelf.users = users;
blockSelf = nil;
}
[usersViewController fetchUsers];
See also: Transitioning to ARC Release Notes
Should I Use weakSelf In a dispatch Block?
You need to use the weak-strong "dance" to avoid a retain cycle only when there's a persistent retain cycle. For that to happen, two conditions need to be met:
- the Block is owned by an object that is referenced inside the Block
- the owner doesn't release the Block before its own deallocation
If either one of those things is not true, there is no persistent retain cycle, and no problem.
In this case, neither is true. In general, Blocks put onto dispatch queues will not be subject to retain cycles, unless you're keeping the Block around in an ivar to reuse.
weakSelf on queue calling method that use self inside
Yes. You're good there. Only variables inside the block itself are retained.
DispatchQueue.main.async { [weak self] in in Objective C?
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
// use weakSelf here
});
Difference between weak self vs weak self()
You do not pass [weak self] ()
as an argument to a closure.
[weak self]
is a capture list and precedes the- parameter list/return type declaration
() -> Void
in the closure expression.
The return type or both parameter list and return type can be omitted if they can
be inferred from the context, so all these are valid
and fully equivalent:
dispatch_async(dispatch_get_main_queue()) { [weak self] () -> Void in
self?.doSomething()
}
dispatch_async(dispatch_get_main_queue()) { [weak self] () in
self?.doSomething()
}
dispatch_async(dispatch_get_main_queue()) { [weak self] in
self?.doSomething()
}
The closure takes an empty parameter list ()
and has a Void
return type.
Why is [weak self] or [unowned self] not needed in Operation Queue?
You do have a retain cycle there, but that doesn’t automatically lead to a memory leak. After the queue finished the operation it releases it thus breaking the cycle.
Such an temporary retain cycle can be very useful in some situations as you won’t have to hang on to an object and still have it finish its work.
As an experiment you can suspend the queue. Then you will see the memory leak.
Is [Weak self] always needed when dealing with URLSession?
In the example you've given, [weak self]
is potentially unnecessary. It depends on what you want to happen if ViewModel
is released before the request completes.
As noted in the URLSessionDataTask
docs (emphasis mine):
After you create a task, you start it by calling its resume() method. The session then maintains a strong reference to the task until the request finishes or fails; you don’t need to maintain a reference to the task unless it’s useful for your app’s internal bookkeeping.
The session has a strong reference to the task. The task has a strong reference to the closure. The closure has a strong reference to the ViewModel
. As long as the ViewModel
doesn't have a strong reference to the task (which it doesn't in the code you provided), then there's no cycle.
The question is whether you want to ensure that the ViewModel
continues to exist long enough for the closure to execute. If you do (or don't care), then you can use a simple strong reference. If you want to prevent the task from keeping the ViewModel
alive, then you should use a weak reference.
This is how you need to think about reference cycles. There is no general rule "use weak
here." You use weak
when that's what you mean; when you don't want this closure to keep self
around until it is released. That particularly is true if it creates a cycle. But there's no general answer for "does this create a cycle." It depends on what pieces hold references.
This also points to where your current API design is not as good as it could be. You're passing didLoadData
in init
. That very likely is going to create reference cycles and force your caller to use weak
. If instead you made didLoadDdata
a completion handler on loadUser()
, then you could avoid that problem and make life easier on the caller.
func loadUser(completion: @escaping ((User?) -> Void)? = nil) {
service.fetchUser {
self?.user = user
didLoadData?(user)
}
}
(Your current API has a race condition in any case. You're starting loadUser()
before didLoadData
can be set. You may be assuming that the completion handler won't complete before you've set dataDidLoad
, but there's no real promise of that. It's probably true, but it's fragile at best.)
Are there potential drawbacks to using a [weak self] within a closure after declaring a strong self?
Let's take this step by step (line by line)
// Weak 1
runClosure { [weak self] in
The first line creates a reference to the target object, a reference which coincidentally (or not) is named self
.
self?.isInner = false
The above line makes use of the weak reference, it has no effects on the target object lifecycle.
guard let strongSelf = self else { return }
Now, this line indeed creates a strong reference to the target object, which will extend objects's lifetime by at least the lifetime of strongSelf
. Now depending on the compiler aggressiveness, strongSelf
might die after passing this line (the last reference in code), or when the outer closure finishes executing.
// Can this [weak self] create problems?
// Weak 2
strongSelf.runClosure { [weak self] in
Now this has almost exactly the same effect as the capture from the outer closure. It creates a reference to a possibly already deallocated instance (depending if strongSelf
is still alive at this point or not).
self?.isInner = true
This is the regular optional usage, no effects on target lifetime.
Now, assuming that runClosure
runs asynchronously, there are no issues with the target object living more that expected (assuming there aren't any more strong references out there).
To summarize, an object lifetime is determined by the number of strong references exist to that object. Once all strong references are destroyed, the object will be automatically deallocated. In your particular scenario, the inner closure weakly captures and already weak reference, and this doesn't impact the lifecycle of the target object. The only player is strongSelf
, which gets destroyed no later than the outer closure destroyal.
Related Topics
Iphone: Where the .Dsym File Is Located in Crash Report
Resizing a Uilabel to Accommodate Insets
In Swift How to Call Method with Parameters on Gcd Main Thread
iOS 7.0 No Code Signing Identities Found
Create a Rectangle with Just Two Rounded Corners in Swift
How to Add a 'Done' Button to Numpad Keyboard in iOS
How to I Import 3Rd Party Frameworks into Xcode Playground
Dismissmodalviewcontrolleranimated Deprecated
View with Continuous Scroll; Both Horizontal and Vertical
Draw Dotted (Not Dashed!) Line, with Ibdesignable in 2017
Uilocalnotification Is Deprecated in iOS 10
Add Entry to iOS .Plist File via Cordova Config.Xml
How to Set Image for Bar Button with Swift
Remove Text from Back Button Keeping the Icon