INSTRUMENTSARC Memory Management Hanging pointer
Previous blog Ios/os x Memory Management (a): The basic concepts and principles of IOS/OSX memory management in the reference count and memory management rules, and the introduction of the ARC new memory management mechanism after the selection of ownership qualifiers (__strong, __ Weak, __unsafe_unretained, and __autoreleasing) to manage memory. This article focuses on what memory management problems are encountered in real-world development and how to use tools to debug and resolve them.
Before looking down, please download the example Memoryproblems, we will use this project to expand how to check and resolve memory problems.
Hanging pointer problem
The hanging pointer (dangling Pointer) is a pointer to an object that has been disposed or reclaimed, but does not make any changes to the pointer (in general, it points to a null pointer), but still points to an address that has already been reclaimed. If the object pointed to by the pointer has been released but is still in use, it will cause the program to crash.
When you run Memoryproblems, click the Hang pointer option and the Exc_bad_access crash message appears.
Let's see what this namelistviewcontroller is for. It inherits Uitableviewcontroller, which mainly displays information of multiple names. Its implementation file is as follows:
static nsstring *const knamecellidentifier = @ "Namecell"; @interface namelistviewcontroller () #pragma mark - Model@property (strong, nonatomic) nsarray *namelist; #pragma mark - Data source@property (assign, nonatomic) ArrayDataSource *dataSource; @end @implementation namelistviewcontroller- (void) viewdidload { [super viewdidload]; self.tableview.datasource = self.datasource;} #pragma mark - Lazy initialization- (nsarray *) namelist{ if (!_namelist) { _namelist = @[@ "Sam", @ "Mike", @ "John", @ "Paul", @ "Jason"]; } Return _namelist;} - (arraydatasource *) datasource{ if (!_datasource) { _datasource = [[arraydatasource alloc] initwithitems:self.namelist cellidentifier: knamecellidentifier tableviewstyle: uitableviewcellstyledefault configurecellblock:^ (Uitableviewcell *cell, nsstring *item, nsindexpath *indexpath) { cell.textlabel.text = item; }]; } return _datasource;} @end
To display data through TableView, the first thing to do is to implement the Uitableviewdatasource protocol, in order to slim the controller and reuse data source, I separated it into a class arraydatasource to implement the Uitableviewdatasource protocol. The DataSource is then assigned to Tableview.datasource in the Viewdidload method.
After explaining Namelistviewcontroller's responsibilities, we need to think about the causes and location of exc_bad_access errors.
In general, the cause of exc_bad_access errors is caused by hanging pointers, but it is not certain which pointers are hanging pointers, because the console does not give specific crash information.
Enable nszombieenabled
To get more crash information, you need to start nszombieenabled. The steps are as follows:
1. Select the edit Scheme and click
2.Run, diagnostics, Enable Zombie Objects
Once set up, run and click the hanging pointer again, although it will crash again, but this time the console prints the following useful information:
Message sent to deallocated instance 0x7fe19b081760 the idea is to send information to a disposed object, that is, the disposed object also calls a method. Now we probably know what causes the program to crash, but specifically which object is released and still used?
Click the Continue Program execution button on the red box above to continue running as follows:
Notice the two red boxes above, their two addresses are the same, and Arraydatasource has a _nszombie_ modifier in front of it, stating that the DataSource object is released and still in use.
Look further datasource the modifier for the Declaration property is assign
#pragma mark-data Source@property (assign, nonatomic) Arraydatasource *datasource;
and assign correspondence is __unsafe_unretained, it is similar to __weak, it modifies the variable does not hold the ownership of the object, but when the variable points to the object's RC is 0 o'clock, the variable is not set to nil, but continue to save the object's address.
Therefore, in the Viewdidload method
-(void) viewdidload {[Super viewdidload]; Self.tableView.dataSource = Self.datasource; /* As DataSource is modified by assign, the object of the object is released immediately after the Self.datasource is assigned, * And Self.tableView.dataSource is not strong, but weak, at this time still use, all will cause the program crash */}
After analyzing the reason and locating the error code, as to how to modify, I think we all know clearly, if you do not know, leave a message to me.
Memory leak issues
Remember the previous article Ios/os x memory Management (a): The basic concepts and principles of the reference cycle example? It can lead to memory leaks, last time just text description, not intuitive, this time we try to use instruments inside the leaks to check memory leaks.
Static analysis
In general, we can check the code for bugs by clang Static Analyzer (static analysis) before the program is run. For example, memory leaks, file resource leaks, or access to null pointer data. Here's an example of a static analysis that tells you how to enable static analysis and what bugs static analysis can look for.
After starting the program, click on static analysis and the crash will appear immediately.
At this point, even if you enable nszombieenabled, the console will not be able to print more information about the bug, what the reason is, and so on.
Open Staticanalysisviewcontroller, which refers to a code example of the Facebook infer tool that contains bugs that will appear in your daily development:
@implementation StaticAnalysisViewController#pragma mark - Lifecycle- (void) Viewdidload{ [super viewdidload]; [self memoryleakbug] ; [self resoureleakbug]; [self parameternotnullcheckedblockbug:nil]; [self npeinarrayliteralbug]; [self prematurenilterminationargumentbug];} #pragma mark - Test methods from facebook infer iOS Hello examples- (void) memoryleakbug{ cgpathref shadowpath = Cgpathcreatewithrect (self.inputview.bounds, null);} - (void) Resoureleakbug{ file *fp; fp=fopen ("Info.plist ", " R ");} -(void) parameternotnullcheckedblockbug: (void (^) ()) Callback { callback ( );} -(nsarray*) npeinarrayliteralbug { nsstring *str = nil; return @[@ "Horse", str, @ "Dolphin"];} -(nsarray*) prematurenilterminationargumentbug { nsstring *str = nil; return [nsarray arraywithobjects: @ "Horse", str, @ " Dolphin ", nil];} @end
The following is a static analysis to check if the code exists bugs. There are two ways of:
The static analysis results are as follows:
Using the static analysis results, let's analyze why nszombieenabled cannot locate the exc_bad_access error code location. Since callback is passed in with a null pointer, and nszombieenabled can only target an address that has been disposed of, the startup nszombieenabled cannot be located, but can be learned by static analysis.
Start instruments
Sometimes it is possible to check for some memory leaks using static analysis, but sometimes only the runtime uses instruments to check, and the boot instruments steps are as follows:
1. Click Product---profile on the Xcode menu bar to start instruments
2. At this point, the instruments tool set appears, check the leaks sub-tool click
3. After opening the leaks tool, click the Red dot button to start the leaks tool, and when the leaks tool starts, the simulator or the real machine will follow.
4. When the leaks tool is started, it logs memory allocation information and checks for memory leaks while the program is running. A memory leak occurs when you click on the reference loop to go to that page and then return to the homepage.
Memory leaks. gif
If a memory leak occurs, how do we locate where and why a memory leak occurs?
Locating memory leaks
With leaks you can quickly locate memory leaks, in this example, the steps are as follows:
Analyze the cause of memory leaks
The location of the memory leak code is already positioned above, and what is the reason? You can view the previous article Ios/os x memory Management (a): A circular reference example of basic concepts and principles, where detailed explanations have been given.
Difficult to detect block reference loops
Most memory problems can be detected by static analysis and instrument leak tools, but there is a block reference loop that is difficult to detect, see our block memory leak example, similar to the above hanging pointer example, just in Configurecellblock inside calls a method Configurecell.
- (arraydatasource *) datasource{ if (!_datasource) { _datasource = [[arraydatasource alloc] initwithitems: self.namelist cellidentifier:knamecellidentifier tableViewStyle:UITableViewCellStyleDefault configurecellblock:^ (Uitableviewcell *cell, nsstring *item, nsindexpath *indexpath) { cell.textLabel.text = item; [self configureCell]; }]; } return _datasource;} - (void) Configurecell{ nslog (@ "Just for test");} - (void) Dealloc{ nslog (@ "Release blockleakviewcontroller");}
Let's start with a static analysis to see if we can check out the memory leaks:
As a result, there is no memory leak hint, and we use the Instrument leak tool at runtime to see if we can check out:
As a result, as with static analysis, there is no hint of any memory leak information.
So how do we know that this blockleakviewcontroller has a memory leak? Or is it based on the basic principle of Ios/os X memory management mechanism: When an object has a reference count of 0 o'clock, it automatically calls the-(void) Dealloc method.
In this example, if Blockleakviewcontroller is Navigationcontroller pop out, there is no call to the Dealloc method, stating that one of its property objects is still held and not released. And I print the release blockleakviewcontroller information in the Dealloc method:
-(void) dealloc{NSLog (@ "Release Blockleakviewcontroller");}
After I clicked the Back button, it didn't print out, so there was a memory leak in the Blockleakviewcontroller. As to how to solve the block memory leaks This problem, a lot of basic solid students know how to solve, do not understand the words, their own data to solve it!
Summarize
In general, when creating a project, I will enable analyze During ' build ' in build settings, which will be automatically analyzed at compile time. In this case, after writing a short piece of code, you immediately know if there is a memory leak or other bug problem, and you can fix bugs. In the run process, if exc_bad_access appears, enable nszombieenabled, see if an exception occurs, the console can print more prompt information. If you want to see if there is a memory leak at run time, use the Instrument leak tool. But some memory leaks are hard to check out, sometimes only by manually overwriting the Dealloc method to see if it eventually has no call.
Transferred from: http://www.cocoachina.com/ios/20160222/15333.html
Ios/os X solve memory problems with tools