About Sagit. Framework solution: IOS Memory leakage (lower) caused by two-way reference-self, sagit. frameworkios
Preface:
After processing the Framework Memory leakage, refer to the previous article: About Sagit. Framework solution: IOS Memory leakage caused by bidirectional reference (medium)-IOS unknown Bug
It is found that the memory of the Business Code is not released, and the reason is very simple:
Self is used in the block, resulting in two-way reference, and then we begin to think about how to solve this problem.
In general, the thinking is to change the code. block should not use self, or only use weak references of self.
The framework is special here. There is a very useful series, the STLastXXX series, which is defined by macros and points to self.
I hope the STLastXXXX macro definition series can be used everywhere without restrictions.
So we started to make the effort:
Tossing one: how to redefine the macro in the code?
The above code, to put it bluntly, uses self. Let's take a look at the macro definition:
The common practice is that when a block is encountered, it is accompanied by WeakSelf, like this:
Default STWeakSelf definition:
// Reference in block # define STWeakSelf _ weak typeof (self) selfWeak = self ;__ weak typeof (selfWeak) this = selfWeak; # define STWeakObj (o) _ weak typeof (o) o # Weak = o; # define STStrongObj (o) _ strong typeof (o) o = o # Weak;
To put it bluntly, the macro definition is a text replacement game during the preparation period. What if I redefine the sagit macro In the first line of code in the block?
For example:
Then use the following code:
It seems that I think too much about it, and I can't even make edits.
Tossing 2: Pointing macro definitions to a function
For example:
#define sagit [Sagit share].Layout
Then run to the Sagit class to define a UIView * Layout attribute, for example:
Then, assign the self. baseView value to each base class (STController or STView) during initialization.
PS: baseView is an extension of UIView and UIViewController. It points to View.
For example:
It looks a bit effective, but in this way, you have to think more comprehensively:
1: Each STView in the frame is a baseView. 2: STView can be nested infinitely. 3: therefore, when STView is initially converted, set it to baseView. After loading, if STView has a parent STView and returns control (nested control flow is involved here, if each sub-View is asynchronously loaded, it will be miserable ). 4: I have not inherited the STView. How can I intercept the start and end of the View? (Sagit has gradually weakened the functions of the base class. Most of the functions are extended on the native, so STView is not needed and many functions can be used normally)
Well, it's okay to write so much, but you have to think more carefully !!!!
In addition, this method only avoids self, and self is not allowed to exist.
So, this method should be put for the moment to see if there is any way to break the cycle reference of self first.
Tossing 3: Think about how to break the two-way reference of block and self?
Block and self are strongly referenced by each other. To break it, one party must show weakness.
Since the block must allow the self to be stored, it means that the block must be a strong reference to the self, so you can only think about it. If you make the self to the block, it can only be a weak reference, or no reference!
First, let's look at a piece of code of the framework:
The code to be circled provides the following functions:
In Sagit, it is easy to implement the table code, but the tutorial has not been written yet. It is leaked in advance.
The focus is to extend the Block attribute of AddCell in UITableView. See the code below:
@ Interface UITableView (ST) # pragma mark core extension typedef void (^ AddTableCell) (UITableViewCell * cell, NSIndexPath * indexPath); typedef BOOL (^ DelTableCell) (UITableViewCell * cell, NSIndexPath * indexPath); typedef void (^ AfterTableReloadData) (UITableView * tableView );//! The Cell used to append each row of the Table@ Property (nonatomic, copy) AddTableCell addCell;//! Cell @ property (nonatomic, copy) DelTableCell delCell used to remove rows from a Table ;//! Used to trigger @ property (nonatomic, copy) AfterTableReloadData afterReload after loading Table reloadData ;//! Obtain the Table data source @ property (nonatomic, strong) NSMutableArray <id> * source ;//! Set the Table data source-(UITableView *) source :( NSMutableArray <id> *) dataSource;
Although an addCell attribute is defined, how to hold it depends on how getter and setter implement it:
@ Implementation UITableView (ST) # pragma mark core extension-(AddTableCell) addCell {// returned from a third party}-(void) setAddCell :( AddTableCell) addCell {if (addCell! = Nil) {// third-party holder addCell} else {}}
If the archive addCell block is stored in a place unrelated to UITableView?
If a third party holds a block, as long as the third party does not have a direct relationship with self, there should be no problems.
The reference relationship becomes:
Third party: strong reference = "blockblock: strong reference =" self
If the third party is alive, the block will not be released. If the block is alive, the self will not be released.
So the conclusion is: do not release.
That is to say, a piece of code is written down. Although the relationship is removed, it is still not released.
What if a third party has weak block references?
For this experiment, I created a new project and tried it for 24 hours:
The expected results are not obtained during the experiment,But understand the principle of block and recognize their own logic misunderstandings.
The knowledge gained from the experiment will be shared later. Here we will continue:
Because addCell must survive all the time, you may have to reloadData for UITableView again. Therefore, the addCell block must be strongly referenced, and its lifecycle must be consistent with that of UITableView. Therefore, to break this core, we still have to trigger a third-party behavior event to break the relationship, and the story is changed to: Remove a third party from self. If an event is required to trigger the relationship, no third party is necessary. Because normal A and B reference each other without solution, it means that they have no solution to each other, but as long as A third party exists, it will solve one of them by setting nil.
Finally, the Framework Code is as follows:
-(AddTableCell)addCell{ return [self key:@"addCell"];}-(void)setAddCell:(AddTableCell)addCell{ if(addCell!=nil) { addCell=[addCell copy]; [self key:@"addCell" value:addCell]; } else { [self.keyValue remove:@"addCell"]; }}
Strong references are still used to archive blocks. Here is a note:
If you want to persist a block, copy it first. Otherwise, you will die badly.
Well, let them strongly reference each other. The rest is who will be the third party and how to lift this relationship !!!
Therefore, go back to the navigation bar to intercept the event and destroy it:
@ Implementation UINavigationController (ST) # pragma mark NavigationBar protocol. When the // fuck shouldPopItem method is triggered here, only the navigation bar is triggered and the interface view is not backed up. //-(BOOL) navigationBar :( UINavigationBar *) navigationBar shouldPopItem :( UINavigationItem *) item // same as push methods // {////// reset the navigation of the previous Controller (otherwise, the Pop will Crash after the second Push) /// NSInteger count = self. viewControllers. count; // if (count> 0) // The returned viewControllers is removed from the current Controller. /// {// UIViewController * preController = self. viewControllers [count-1]; // obtain the previous controller /// if ([preController needNavBar]) //// {/// [preController reSetNav: self]; /// //} // return YES; // return to the current page-(void) navigationBar :( UINavigationBar *) navigationBar didPopItem :( UINavigationItem *) item {// if (navigationBar! = Nil & [navigationBar. lastSubView isKindOfClass: [UIButton class]) // {// [navigationBar. lastSubView height: 0]; // cancel UIButton with custom overwrite //} NSInteger count = self. viewControllers. count; if (count> 0) {UIViewController * current = self. viewControllers [count-1]; self. navigationBar. hidden =! [Current needNavBar]; if (self. tabBarController! = Nil) {self. tabBarController. tabBar. hidden =! [Current needTabBar];} // checks whether the previous controller has been released.UIViewController * nextController = current. nextController; if (nextController! = Nil) {[nextController dispose]; nextController =Nil ;}}-(Void) dealloc {NSLog (@ "UINavigationController relase->%@", [self class]);}
Sagit framework: the dispose method is extended for each view and controller. Clearing the key-value pairs is equivalent to setting the block to nil and removing the relationship.
In addition to navigation back, you also need to block one more event, that is, when the presentViewController event jumps, you also need to detect and destroy.
After completing these two steps, you can easily write self in the block,Love references. There is a third party at the end of the story..
Strong references also offer the following benefits:
1: WeakSelf is no longer used. 2: because it is a strong reference, you don't have to worry about it: there is also a StrongSelf set in it, to avoid the flashback problem caused by self removal when multithreading is avoided.
Finally, let's talk about another pitfall of IOS, and the mysterious dealloc method:
As mentioned in the previous article, if the dealloc method is extended for UIView, the resulting murder case is:
Navigation Bar: the system will crash after the second retreat.
This article was also discovered by me. If the dealloc method is extended for UIViewController, the resulting murder case is:
UIAlertView: flashes back when alertViewStyle is set to a text box.
First, open dealloc in the figure below:
Then the running result is as follows:
The last time we had experience, we had to create a new project and use code exclusion to sort the project to dealloc.
If you see this article, you can fool your colleagues again.
Summary:
It may be an illusion that the Sagit framework is much more efficient after the memory is released.
The start-up of IT is also continuing. You are welcome to stay tuned. Thank you!