As we know we need to use a weak reference inside a block to break the retain cycle, like so:
__weak id weakSelf = self;
[self doSomethingWithABlock:^() {
[weakSelf doAnotherThing];
}]
However weak references can not break the retain cycle caused by a NSTimer.
__weak id weakSelf = self;
timer = [NSTimer scheduledTimerWithTimeInterval:30.0f
target:weakSelf
selector:#selector(tick)
userInfo:nil
repeats:YES]; // No luck
What's the difference? How can the timer still retain the target?
The whole problem with the selector-based NSTimer technique is that it establishes a strong reference to the object you pass to it. So whether the variable you used to hold the reference to the target you passed to scheduledTimerWithTimeInterval was, itself, strong or weak, is immaterial. Assuming the target reference wasn't nil by the time the selector-based scheduled timer was scheduled, NSTimer will establish its own strong reference. The "weak" vs "strong" nature of the references in calling code only dictates where ARC will place its own memory management calls in the caller's code, but the target is just a simple pointer, and none of this weak vs strong information is conveyed to NSTimer. The selector-based NSTimer will establish its own strong reference that is not resolved until the timer is invalidated.
This is why, when we want to invalidate a timer built via the selector-based method, we have to it in viewDidDisappear or the like, rather than dealloc.
Note, scheduledTimerWithTimeInterval now has a block-based variation for iOS 10 and later, so you can enjoy the weak reference pattern of blocks if you don't have to support earlier iOS versions:
typeof(self) __weak weakSelf = self;
[NSTimer scheduledTimerWithTimeInterval:30 repeats:true block:^(NSTimer * _Nonnull timer) {
// use weakSelf here
}];
Related
In a class, I've declared a thread like:
#property (nonatomic, strong) dispatch_queue_t databaseQueue;
and then I perform an operation this thread like
dispatch_async(self.databaseQueue, ^{
[self.dao deleteRetries];
});
Can this potentially create a retain cycle?
AND
the current class holds a strong reference to viewControllerToDismiss and there is a code which looks like:
[viewControllerToDismiss dismissViewControllerAnimated:shouldAnimateDismiss completion:^{
[self performSomeAction];
}
is this a retain cycle?
It is merely a strong reference to self that is eliminated automatically when the block finishes running and GCD releases the block. Note, this is a strong reference between the queue object itself, the block, and self, but not to databaseQueue. E.g. even if databaseQueue was some local reference that had fallen out of scope after you dispatched but before it ran, you'd still have a strong reference between the queue object, the block, and self.
If you don't want that strong reference at all, use weakSelf pattern:
typeof(self) __weak weakSelf = self;
dispatch_async(self.databaseQueue, ^{
[weakSelf.dao deleteRetries];
});
You asked:
Please could you elaborate more on "Note, this is a strong reference between the queue object itself, the block, and self, but not to databaseQueue"?
Consider:
- (void)runManyTasks {
dispatch_queue_t queue = dispatch_queue_create("com.domain.app.foo", 0);
for (NSInteger i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self doSomething];
});
}
}
- (void)doSomething {
[NSThread sleepForTimeInterval:1];
}
Even though I have no references in my code to that local variable, queue, after runManyTasks finishes, if I call runManyTasks, GCD will keep its own strong reference to the actual underlying queue object until all tasks finish, and the queue will keep copies of those blocks until they finish running, and those blocks will maintain a strong reference to self until GCD finishes with all of them (roughly 10 seconds, in this example).
You go on to edit your question and ask:
the current class holds a strong reference to viewControllerToDismiss and there is a code which looks like:
[viewControllerToDismiss dismissViewControllerAnimated:shouldAnimateDismiss completion:^{
[self performSomeAction];
}
is this a retain cycle?
For all practical considerations, no. That block is released as soon as the dismissal animation is done, so you generally wouldn't complicate your code with weakSelf pattern here. In fact, that view controller isn't dismissed until the animation finishes, anyway, so there's absolutely nothing gained from weakSelf pattern (other than making code more convoluted).
First, you have not declared a thread. It’s a queue, that’s something different. (Fortunately, as working directly with threads is a pain.)
You are dispatching a block into the queue. The block retains self and the queue retains the block, which means you do have a retain cycle, since the queue, being a strong property, is retained by self:
self -> queue -> block -> self -> queue -> …
BUT the block should be short-lived, judging by the API. When the block is finished, it will get released from the queue, breaking the retain cycle. So I would not worry about a case like this.
Using self directly in blocks will create strong retain cycle.
To avoid retain cycle, please check below code
__weak YourViewController *weakSelf = self;
dispatch_async(self.databaseQueue, ^{
if (weakSelf){
YourViewController *strongSelf = weakSelf;
[strongSelf.dao deleteRetries];
}
});
For more info visit this link Working with blocks
In my app I have a following piece of code:
__weak __typeof(self)weakSelf = self;
_pingTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
repeats:YES
block:^(NSTimer * _Nonnull timer)
{
__strong __typeof(weakSelf)strongSelf = weakSelf;
[strongSelf pingWithBlock:nil];
}];
this works perfectly in iOS 10+, but I need the app to support iOS 9 as well. So I needed to provide a method that would work for both.
I tried this:
__weak __typeof(self)weakSelf = self;
_pingTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
target:weakSelf
selector:#selector(pingWithBlock:)
userInfo:nil
repeats:YES];
pingWithBlock method is defined in the same class, it's an instance method.
But this doesn't seem to work, meaning I get a bad memory access crash.
If anyone has any suggestions it will be highly appreciated.
EDIT:
thanks to #dgatwood explanations code below fixes the issue
- (void)autoPing
{
_pingTimer = [NSTimer scheduledTimerWithTimeInterval:self.autoCheckInterval
target:self
selector:#selector(pingWithBlock)
userInfo:nil
repeats:YES];
}
-(void)pingWithBlock
{
[self pingWithBlock:nil];
}
This is kind of odd. NSTimer retains its target. Maybe that doesn't happen in this case because of the __weak, but I thought it did anyway. *shrugs*
Either way, this sounds like a multithreading race condition:
Your timer isn't retaining the object, so it could go away at any time.
Something else is retaining the object.
The timer is scheduled in the runloop of the thread that was running when the timer was constructed.
That something else disposes of the reference to the object in another thread.
The timer fires in the first thread and the zeroing weak reference hasn't zeroed because the object is still halfway through destroying itself.
A crash occurs.
The best fix is to let the timer retain the target object (by removing all the weakSelf stuff). If the timer is a repeating timer, provide a method to allow the code that disposes of the enclosing object to cancel that timer, and be careful to always call it.
This question already has answers here:
Strong reference to a weak references inside blocks
(2 answers)
Closed 6 years ago.
I am trying to understand this code :
__weak LoginViewController *weakSelf = self;
NSTimer *networkTimer = [NSTimer pym_scheduledTimerWithTimeInterval:15.0 block:^{
LoginViewController *strongSelf = weakSelf;
[strongSelf timeout];
} repeats:NO];
[PYMAuthManager loginWithUsername:username password:password completionHandler:^(BOOL successful) {
if (successful) {
[networkTimer invalidate].......
it's for a network timer, that will timeout after 15 seconds if no activity. Why is the pointer *strongSelf = weakSelf created in the block? Is it not okay just to use [weakSelf timeout]? I understood that whenever accessing self in a block we have to use a weak reference, why is another pointer created here? Any help would be great, thank you.
Edit: Updated to better reflect actual system semantics (as pointed out by Darren).
By using weakSelf in the block, you are avoiding retain cycles. If you were to replace the block code with [weakSelf timeout], there would be no difference; the object is retained for the duration of the timeout execution.
If you had instead used __unsafe_unretained to create your weakSelf, it's possible that the machine could release the memory back to the system and cause your app to EXC_BAD_ACCESS while executing timeout. Creating a strong reference in this instance would avoid this error.
When you create a strong reference, it is held within its scope; in this case, for the duration of the block. Because your block only effectively executes one statement, the replacement to [weakSelf timeout] causes no harm. If you had multiple statements that all used weakSelf or relied on the state of weakSelf, then you would want to create a strong reference for the scope of all of the statements.
I thought I understood the usage of self in a block that is copied is a no no.
But in an attempt to clean my code i enabled a bunch of warnings in Xcode, one called "Sending messages to weak pointers"
so now in all my blocks, every time I use my created weakself reference __weak typeof(self) weakself = self;
I get this warning: Weak receiver may be unpredictably set to nil
a trivial example:
__weak typeof(self) weakself = self;
[aClass doSomethingInABlock:^{
[weakself doSomething]; //warning.
}];
I have seen answers which define a version of self within the block like so:
__weak typeof(self) weakself = self;
[aClass doSomethingInABlock:^{
typeof(self) selfref = weakself;
[selfref doSomething]; //no warning.
}];
So I'm wondering what actually happens here:
Am I just tricking the compiler?
what does a strong reference to a weak reference do?
anything else I'm missing.
Thanks.
I thought I understood the usage of self in a block is a no no.
This is not strictly correct. Blocks retain the objects in them, so don't use self in a block if your block is retained by self.
For example, you can use self just fine in a UIView animation block. This is because your view controller (or whatever code is calling the animation) doesn't have a pointer to the UIView animation block.)
Am I just tricking the compiler?
No.
What does a strong reference to a weak reference do?
If the weak reference is not nil, the retain count of the receiver is increased. This will stop the object from being deallocated while you're using it.
Remember, ARC will deallocate objects when there are no longer strong references to them. By creating a strong reference inside the block, you're preventing possible deallocation until you're done with it.
anything else I'm missing.
I recommend reading the Practical Memory Management section of the Advanced Memory Management Programming Guide. Especially, read the subsection "Use Weak References to Avoid Retain Cycles".
Remember that in ARC, an object will not be deallocated as long as there's a strong reference to it.
When an object has a weak reference, the object might be deallocated (if there's no other strong reference to the same object), so a weak reference doesn't ensure the objects life.
By doing this:
typeof(self) selfref = weakself;
you're ensuring that you have a strong reference to that object before you use it (selfref is the strong reference pointing to the same object weakself is referring to). If you don't do this, the object could be deallocated while you use it.
Strong reference to a weak reference retains an object. It could be important in following case
__weak typeof(self) weakself = self;
[aClass doSomethingInABlock:^{
[weakself allocateSomething]; // (1)
// ..... code (2)
[weakself freeSomething]; // (3)
}];
If Weak receiver will be unpredictably set to nil in line (2) resources could be successfully allocated in (1) but not freed in (3). To avoid such problems strong reference could be used.
[aClass doSomethingInABlock:^{
typeof(self) selfref = weakself;
[selfref allocateSomething]; // (1)
// ..... code (2)
[selfref freeSomething]; // (3)
}];
Now if selfref is not nil in (1) it will also be valid in (2) and (3).
When I execute this code:
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(showButtons) userInfo:nil repeats:NO];
do I need to nil it or release it, ot whatever for memory management?
I am using ARC
Yes, NSTimer will maintain a strong reference to the target, which can cause (especially in repeating timers) strong reference cycles (a.k.a. retain cycles). In your example, though, the timer does not repeat, and is delayed only 0.5, so worst case scenario, you will have a strong reference cycle that will automatically resolve itself in 0.5 seconds.
But a common example of an unresolved strong reference cycle would be to have a UIViewController with a NSTimer property that repeats, but because the NSTimer has a strong reference to the UIViewController, the controller will end up being retained.
So, if you're keeping the NSTimer as an instance variable, then, yes, you should invalidate it, to resolve the strong reference cycle. If you're just calling the scheduledTimerWithTimeInterval, but not saving it to an instance variable (as one might infer from your example), then your strong reference cycle will be resolved when the NSTimer is complete.
And, by the way, if you're dealing with repeating NSTimers, don't try to invalidate them in dealloc of the owner of the NSTimer because the dealloc obviously will not be called until the strong reference cycle is resolved. In the case of a UIViewController, for example, you might do it in viewDidDisappear.
By the way, the Advanced Memory Management Programming Guide explains what strong reference cycles are. Clearly, this is in a section where they're describing the proper use of weak references, which isn't applicable here (because you have no control over the fact that NSTimer uses strong references to the target), but it does explain the concepts of strong reference cycles nicely.
If you don't want your NSTimer to keep a strong reference to self, in macOS 10.12 and iOS 10, or later, you can use the block rendition and then use the weakSelf pattern:
typeof(self) __weak weakSelf = self;
[NSTimer scheduledTimerWithTimeInterval:0.5 repeats:false block:^(NSTimer * _Nonnull timer) {
[weakSelf showButtons];
}];
By the way, I notice that you're calling showButtons. If you're trying to just show some controls on your view, you could eliminate the use of the NSTimer altogether and do something like:
self.button1.alpha = 0.0;
self.button2.alpha = 0.0;
[UIView animateWithDuration:0.25
delay:0.5
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
self.button1.alpha = 1.0;
self.button2.alpha = 1.0;
}
completion:nil];
This doesn't suffer the retain issues of NSTimer objects, and performs both the delay as well as the graceful showing of the button(s) all in one statement. If you're doing additional processing in your showButtons method, you can put that in the completion block.
If you are saving it in a property, then yes, you do need to set it to nil after it fired the selector.
It's also safe to save it in case your class gets deallocated for whatever reason, so that you can [timer invalidate] if you need to.
Yes, you can use: myTimer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(showButtons) userInfo:nil repeats:NO];
And then in your viewDidDisappear [myTimer invalidate]
Use the new method of Timer that uses closures
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: {[weak self] (timer) in
print("Tick tock")
guard let ws = self else { return }
//some action that repeats
ws.myViewControllerMethod()
})