“This is the third day of my participation in the November Gwen Challenge. See details of the event: The last Gwen Challenge 2021”.
First, the generation of circular references
1.
— : indicates a weak reference.
-> : indicates a strong reference.
Circular reference can be simply understood as object A refers to object B, and object B refers to object A: A -> B -> A. In this case, both parties keep A reference to each other at the same time, resulting in the reference count of both parties is not zero at any time, and both parties cannot release the reference, causing A memory leak.
Of course, it’s not just two objects that refer to each other that form a circular reference, it’s also multiple objects that refer to each other that eventually form a loop.
For example, A – > B > C – >… – > X – > B.
Harm 2.
Cyclic references may cause high memory consumption, memory leakage, poor performance, and flash back of the APP.
Two, easy to create circular reference three scenarios
Block, delegate, NSTimer
1, the delegate
self.tableView.delegate = self; If the delegate uses a strong modifier, it forms a circular reference: self -> tableView -> delegate -> self.
This can be solved by using weak when defining the delegate property: self -> tableView — delegate -> self. There’s no strong reference between the tableView and the delegate, so there’s no loop.
The best way to circumvent delegate loop references is simple: define delegate attributes using assign(MRC) or weak(ARC), and don’t play retain or strong with your hand.
2.block
(1) Not all blocks generate circular references, and it is up to us to determine whether a block generates circular references, for example
// This will not generate a circular reference, because this block is not held by self, it is held by UIView's class object, this block has nothing to do with self, so you can use self arbitrarily.
[UIView animateWithDuration:0.0 animations:^{[self viewDidLoad];
}];
Copy the code
Self -> reachabilityManager -> block -> self
//self -> reachabilityManager -> block -> self are circular references
self.reachabilityManager.stateBlock = ^(int number){
NSLog(@"% @".self. reachabilityManager);
};
// Or (there is no explicit "self" inside the block, as long as you use something that self owns in the block, there will be a circular reference!)
self.reachabilityManager.stateBlock = ^(int number){
NSLog(@"% @"._ reachabilityManager);
};
Copy the code
(3) The solution to block loop reference is to use __weak to modify self, and then use modified weakSelf to replace self in the block:
__weak __typeof(self) weakSelf = self;
[self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"% @",weakSelf.reachabilityManager);
}];
// However, there is a drawback to just using __weak to modify self: __weak may cause memory to reclaim weakSelf early, and weakSelf will be released before NSLog() is executed, and then (null) will be printed when NSLog() is executed.
Copy the code
// So to solve this defect, we need to modify weakSelf with __strong inside the block like this:
__weak __typeof(self) weakSelf = self;
[self.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"% @",strongSelf.reachabilityManager);
}];
Copy the code
We find that the above method does solve all the problems, but there may be two points we don’t understand: using weakSelf and strongSelf, what is the difference between doing so and using self directly? Why aren’t there circular references? This is because weakSelf outside the block is to break the loop circular reference, while strongSelf inside the block is to prevent weakSelf from being released in advance. StrongSelf is only a local variable in the block, which will be recycled after the execution of the block, and will not cause circular reference. What’s the difference between doing this and using weakSelf? The only difference is that there is an extra strongSelf, where strongSelf increases the reference count of self by 1, making self dealloc only after the block has been executed and the local strongSelf has been recycled. The final method solves 99 percent of the block’s circular reference problems. Solution to NSTimer circular references
3. NSTimer
The first thing that comes to mind is to destroy the NSTimer before the OneViewController dealloc, so that the loop is broken. The easiest way to do that is to start NSTimer in viewWillAppear and then destroy NSTimer in viewWillDisappear, in pairs, absolutely no problem.
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.time = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(log) userInfo:nil repeats:YES];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.time invalidate];
self.time = nil;
}
Copy the code
52 methods in Effective Objective-C
#import <Foundation/Foundation.h>
@interface NSTimer (YPQBlocksSupport)
+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats;
@end
#import "NSTimer+YPQBlocksSupport.h"
@implementation NSTimer (YPQBlocksSupport)
+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(ypq_blockInvoke:) userInfo:[block copy]
repeats:repeats];
}
- (void)ypq_blockInvoke:(NSTimer *)timer
{
void (^block)() = timer.userInfo;
if(block) { block(); }}@end
Copy the code
__weak ViewController * weakSelf = self;
[NSTimer ypq_scheduledTimeWithTimeInterval:4.0f
block:^{
ViewController * strongSelf = weakSelf;
[strongSelf afterThreeSecondBeginAction];
}
repeats:YES];
Copy the code
The timer keeps its target object in a loop caused by repeated execution of tasks. Do be careful, and don’t forget to call the invalidate method in the timer when dealloc is in progress.