How to solve the problem of--SIGABRT and exc_bad_access

Source: Internet
Author: User
Tags stack trace uikit gdb debugger

IOS: How to solve the problem of running

The program encounters crash, do not panic, should find the place where the crash-accurate to the file, to which line, rather than the aimless change code, because this will only make the situation worse, error procedures: Http://www.raywenderlich.com/downloads/Problems.zip
There are basically two kinds of crash that occur: SIGABRT and exc_bad_access; SIGABRT is a program-controlled crash, and the app breaks down because the system recognizes that the app does something that the system doesn't support; Exc_bad_access is more difficult to debug because it only happens when the app is in a crash state, usually because of memory management problems.
The first step: in order to find out exactly where your code crashes occur in which file, which line.

(1) Run the program error, type is SIGABRT, this type of error will have error message, can be viewed in the debug output panel of Xcode; Learn to identify error messages because they contain important, faulty clues.
At this point the clue is: [Uinavigationcontroller setlist:]: Unrecognized selector sent to instance 0x6a33840
Here, the problematic object is located in the memory address 0x6a33840uinavigationcontroller, the method is setlist: It is good to know the reason for the crash, but the first step in your action is to make sure that the error occurs in that code. You need to find the name of the source file and the number of lines of code that behaved badly. To do this, you can use the call stack (also known as stack trace or backtracking).
Switch to debug navigation in the left pane of the Xcode window where an application crashes. It shows the threads that are active in the application and emphasizes the crash of the thread. Usually thread 1, the application's main thread, because where you will do most of your work. If your code uses queues or background threads, the application may crash on other threads. The root of Xcode's outstanding problem is the MAIN.M main () function. This doesn't tell you much, so you have to dig deeper. To see more call stacks, click on the bottom leftmost list icon

The call stack calls stack shows the current active method in the app: Start () calls main (), while main () calls Uiapplicationmain (). Uiapplicationmain () invokes the _run method of the UIApplication object, _run method calls __pthread_kill.

All of these functions and method call stacks, except for main (), are grayed out. This is because they are built from within the iOS framework. There is no source code available for them.
Your source code is unique in this stack trace main.m, so this is what xcode the source code Editor shows, although it is not really a real source of crashes.
(2) Using the exception breakpoint to find the line of error

Run the program to find the exact location of the error
Application:didfinishlaunchingwithoptions: Method of
Viewcontroller.list = [Nsarray arraywithobjects:@ "one", @ "two"];
Then look at the error message [Uinavigationcontroller setlist:]: Unrecognized selector sent to instance 0X6D4ED20; then analyze the cause of the error and fix it

Note: Whenever, run get "unrecognized selector sent to instance XXX"; Check the class name of the calling method and the name of the method, possibly because the wrong object invoked the method, or it might be a spelling error.

(3) Run the program again, appear exc_bad_access
First of all, according to the warning given by the system, part fit;
Then you can look at the information in call stack and find some clues to the error, such as the following

Because the exception breakpoint is still available at this point, click the button below to get the error message.

Note: The reason for the error "This class isn't" key value coding-compliant for "key XXX" was most likely that a nonexistent attribute was loaded from the nib file

Part II
Program to run get empty table

(1) using NSLog (), while not ignoring xcode warnings
The expected results have not occurred and there is little technology to solve them, but they can be handled using NSLog (). Use NSLog () to step through to track whether the program's running process conforms to its own set of processes

From the names of these methods, we can guess that this error occurs in some places where the TableView is being repaint. For example, we can see Layoutsubviews and _updatevisiblecellsnow: The methods of these names

Keep running this app to see if you can get some good error messages-– Remember, now just suspend the program while throwing the exception, and not crash. Click the Continue program button, or type the following command in the Debug window:

(LLDB) C

You may have to click a few more times to continue the button, the "C" command is also a short continuation instruction, and click on the Continue button for an effect that is not directly executed to the end.

Now this debugging window is spewing out some of the more useful information:

Terminating app due to uncaught exception ' nsinternalinconsistencyexception ',
reason: ' UITableView dataSource Must return a cell from Tableview:cellforrowatindexpath: '
***firstthrow Call stack:
( 0x13ba0520x154bd0a0x1362a780x99a2db0xaaee30xab5890x96dfd0xa58510x50301
0x13bbe720x1d6492d0x1d6e8270x1cf4fa70x1cf6ea60x1cf65800x138e9ce0x1325670
0x12f14f60x12f0db40x12f0ccb0x12a38790x12a393e0x11a9b0x27220x2695)
terminate called throwing an exception

Well, that's a pretty good clue. Obviously this UITableView data source does not return a valid cell from the Tableview:cellforrowatindexpath: method, so add some debugging output information to the LISTVIEWCONTROLLER.M method to see:

-(uitableviewcell*) TableView: (uitableview*) TableView Cellforrowatindexpath: (nsindexpath*) IndexPath
{
staticnsstring*cellidentifier=@ "Cell";
Uitableviewcell*cell =[tableview Dequeuereusablecellwithidentifier:cellidentifier];
NSLog (@ "The cell is%@", cell);
Cell.textLabel.text =[list ObjectAtIndex:indexPath.row];
return cell;
}

You add a nslog () tag. Run this app again to see what's out there:
PROBLEMS[18420:F803] The cell is (NULL)
As we can see from the above information, call Dequeuereusablecellwithidentifier: Return is nil, which means using "Cell" The cell that is an identifier may not exist (because the app uses the storyboard of the standard cell).
Of course, this is also a silly bug because Xcode has warned you through static compilation: "Prototype cells must have reuse. (Standard cell must have a reusable logo) ". This is a warning not to be overlooked:

Modify: Open Storyboard, select the standard cell (at the top of the TableView, and display a separate cell of Title), and set the cell identifier to "cell": after fixing that, the compiler warning should be gone. Run this app, and now the debug window should print out:

PROBLEMS[7880:F803] the cell is<uitableviewcell:0x6a6d120; frame = (00;32044); Text = ' Title '; Layer =<calayer:0x6a6d240>>
problems[7880:f803] the cell is<uitableviewcell:0x6877620; frame = (00; 32044); Text = ' Title '; Layer =<calayer:0x6867140>>
problems[7880:f803] the cell is<uitableviewcell:0x6da1e80; frame = (00; 32044); Text = ' Title '; Layer =<calayer:0x6d9fae0>>
problems[7880:f803] the cell is<uitableviewcell:0x6878c40; frame = (00; 32044); Text = ' Title '; Layer =<calayer:0x6878f60>>
problems[7880:f803] the cell is<uitableviewcell:0x6da10c0; frame = (00; 32044); Text = ' Title '; Layer =<calayer:0x6d9f240>>
problems[7880:f803] the cell is<uitableviewcell:0x6879640; frame = (00; 32044); Text = ' Title '; Layer =<calayer:0x6878380>>

(2) Change your NSLog ()

The 6 table View cell was created, but nothing was visible on the table. What's going on here. If you click around in the simulator, you will notice that the first of the 6 cell in TableView can be selected. So, obviously cells are there, but they are all empty:

Change the previous nslog () tag:

-(uitableviewcell*) TableView: (uitableview*) TableView Cellforrowatindexpath: (nsindexpath*) IndexPath
{
staticnsstring*cellidentifier=@ "Cell";
Uitableviewcell*cell =[tableview Dequeuereusablecellwithidentifier:cellidentifier];
Cell.textLabel.text =[list ObjectAtIndex:indexPath.row];
NSLog (@ "The text is%@", [list ObjectAtIndex:indexPath.row]);
return cell;
}

Now you print out the contents of your data module. Run this app and see what it shows:

PROBLEMS[7914:F803] The text is (null) problems[7914:f803] "The text is
(null) problems[7914:f803]" The text is (
NULL)
problems[7914:f803] The text is (null)
problems[7914:f803] "The text is (null)
problems[7914:f803] The text is (null)

The above is a good explanation of why there is nothing in the cell to see the reason why: because this text (text) is always nil. However, if you check your code and add a lot of strings to the list array in the Initwithstyle: method:

[List addobject:@ "one"];
[List addobject:@ "two"];
[List addobject:@ "Three"];
[List addobject:@ "Four"];
[List addobject:@ "Five"];

Just like the above, it's a good way to test your assumptions. Maybe you want to look more accurately at what's in this array. Changed earlier in Tableview:cellforrowatindexpath: Inside of the NSLog () for this:

NSLog (@ "Array contents:%@", list);

At least this will show you something. Run this app. If you are not ready to guess what will happen, the debugging window has been printed for you:

PROBLEMS[7942:F803] Array contents: (NULL)
problems[7942:f803] Array contents: (NULL)
problems[7942:f803] Array contents: (NULL)
problems[7942:f803] Array contents: (NULL)
problems[7942:f803] Array contents: (NULL)
problems[7942:f803] Array contents: (NULL)

Haha, your face is suddenly gloomy. The code above doesn't work because you probably forgot to apply for the memory space for this array object first. This "list" is always nil, so call AddObject: and Objectatindex: it doesn't work.

You should allocate space for this list object when your view controller is loaded, so it should be a good choice in Initwithstyle: method. Modify that method to:

-(ID) Initwithstyle: (uitableviewstyle) style
{
if (self==[super Initwithstyle:style])
{
list =[ Nsmutablearray Arraywithcapacity:10];
[List addobject:@ "one"];
[List addobject:@ "two"];
[List addobject:@ "Three"];
[List addobject:@ "Four"];
[List addobject:@ "Five"];
}
returnself;
}

Give it a try. I faint, still nothing. The Debug window output remains:

PROBLEMS[7971:F803] Array contents: (NULL)
...

After so many assumptions and modifications, but still nothing, these are really frustrating, but keep in mind that you may continue until the end until you know all the assumptions. So the question now is initwithstyle: not called.

(3) using breakpoints
You may also put other nslog () flags in your code, but you can actually use another tool: breakpoints (breakpoints). You've seen the exception breakpoint (Exception breakpoint) that the program terminates whenever an exception is thrown. You can actually add other breakpoints, and you can put them anywhere in the code. Once your program runs to the breakpoint, the breakpoint is triggered and the program goes into debug mode.

You can place special breakpoints by clicking the line number in front of the code edit area. The line number before the blue arrow indicates that there is a breakpoint in the row. You can also see this new breakpoint in the Breakpoint Navigator (breakpoint Navigator):

Run this app again. If Initwithstyle: It's really going to be called, then you clicked "Tap Me." button, when the Listviewcontroller is loaded, the app pauses and goes into the debugger.

As you might expect, nothing has happened. Initwithstyle: not called. In fact, this makes sense because view controller is loaded from storyboard (or xib), so it should be initwithcoder: method.

Replace the previous Initwithstyle: Method with Initwithcoder::

-(ID) Initwithcoder: (nscoder*) adecoder
{
if (self==[super Initwithcoder:adecoder])
{
list =[ Nsmutablearray Arraywithcapacity:10];
[List addobject:@ "one"];
[List addobject:@ "two"];
[List addobject:@ "Three"];
[List addobject:@ "Four"];
[List addobject:@ "Five"];
}
returnself;
}

And keep the breakpoint on top of this method to see how it works:

Once you click on the button, the app will go into the debugger:

The above does not mean that the app crashes. It's just paused at this breakpoint. On the left side of the execution stack (if you don't see the execution stack, you may need to switch to the Debug navigator), you can see that you are from buttontapped: here. In this debug navigator, we see a series of Uikit methods implemented, and a new view controller is loaded. (By the way, a breakpoint is a very good tool to point out how this system works.) )

If you want to leave where you were before, keep running the program simply by clicking the Continue program to run the button or by typing "C" in the Debug console.

Obviously, everything was not as we had expected, and the app ran again. I told you, it has a lot of bugs.

Note: Before you proceed, in Initwithcoder: Remove the breakpoint or invalidate the breakpoint. Because he has shown his purpose, so now it can leave.

You can right-click the breakpoint at the point where the line number is displayed, and select Remove breakpoint from the menu that pops up. You can also drag the breakpoint out of the window or remove it from the breakpoint debugger.

If you don't want to remove this breakpoint, you can simply invalidate the breakpoint. To do this, you can use the right click pop-up menu, or click the breakpoint once left. To determine whether this breakpoint is valid, you can look at the color of the breakpoint, when the light blue is not valid, dark blue is valid.

(4) Zombies.

Back to this crash. It is a exc_bad_access, the debugger points to what happened to him in Tableview:cellforrowatindexpath:. You can use a debugging tool that lets you see the dawn: Zombies.

Open the scheme editor:-for this project to select the Run option and then select the Diagnosics tab. Check the Enable Zombie objects option:

Run this app now. The app still crashes, but now you're going to get the following error message:

Problems[18702:f803]***-[__nsarraym Objectatindex:]: Message sent to deallocated instance 0x6d84980

This is what the zombie Enable tool does, a little generalization: whenever you create a new object (by sending a "alloc" message), a piece of memory will be reserved for the object's instance variable. When the object is released, his retention count (retain count) becomes 0, and the memory is released. However, you may still have a lot of pointers pointing to this defunct memory, which is based on the assumption that there is a valid object here. If some part of your program tries to use this wild pointer, the app will crash with the exc_bad_access error.

When the Zombie tool is enabled, even if the object is freed, the object's memory is not cleaned up. So the memory will be labeled "immortal". If you try to use this memory later, the app will be aware of your error and the app is going to throw the "message sent to daellocated instance" error and terminate the operation.

So that's what happened before. This line is the use of undead objects:

Cell.textLabel.text =[list ObjectAtIndex:indexPath.row];

This cell object and his textlabel should be good, then indexpath should be correct, so I guess that under this problem, this undead object should be "list".

You probably already have a good clue to suspect this "list" because the error message says:

-[__nsarraym Objectatindex:]

The class of the Undead object is __nsarraym. If you've had some time cocoa programming experience, you should know some basic classes, like NSString and Nsarray are actually "class clusters", This means that, like nsstring or Nsarray, these primitive classes are replaced by special classes in some of the underlying places. So here you can see some nsarray types of objects, that is, this "list" should actually be a nsmutablearray.

If you want to be sure, you can add a nslog () after the line of code that assigns the "list" array:

NSLog (@ "list is%p", list);

This will print out the same memory address as the error message (in my case is 0x6d84980, but when you test yourself, the address will be different).

You can also use the "P" command in the debugger to print out the address of the "list" variable (and the relative command is "PO", which will print out the actual object, not the address). The convenient place is that you can omit a lot of additional nslog () steps and recompile the app,

(LLDB) P list

Note: Unfortunately, the above commands do not perform well in the xcode4.3. For some reason, this address has always been a display of 0x00000001, probably because of this class cluster bar. Under the GDB debugger, those commands perform well, showing that "list" is zombie in the debugger's variable window. So I think this is a lldb bug.

The place to allocate space for this list array is Initwithcoder: This is the following:

List =[nsmutablearray arraywithcapacity:10];

Since this is not an arc (Automatic Reference counting) project, it is manual memory management, so here you need to retain this variable:

In Initwithcoder:
list =[[nsmutablearray arraywithcapacity:10] retain];

To avoid memory leaks, you also have to release the object in the Dealloc function, as follows:

-(void) dealloc
{
[list release];
[Super Dealloc];
}

Run this app again. It crashes again in this same line, but notice that the output of this debug window changes:

PROBLEMS[8266:F803] Array Contents: (One
,
two,
Three,
Four,
Five
)

From the above information you can see that the array has been allocated memory space and contains a string. This crash tip is no longer exc_bad_access, but SIGABRT, so you need to set this exception breakpoint again. Fix this and continue looking for other bugs.

Note: Even if you use arc, it is a very big thing to do under such a memory management error, and you will crash and get a exc_bad_access error, especially if you use an unsafe retention attribute.

My little proposal: whenever you get a exc_bad_access error, you can open zombie objects and try again.

Note that you should not always enable zombie objects. Because this tool will never release memory, just simply mark the memory as immortal and you will eventually run out of memory at some point. So you should start zombie objects when troubleshooting memory-related errors, and you should turn it off at other times.

(5) Single Step debugging
Rerun this program and click the button. You will enter the debugger the first time you execute Tableview:cellforrowatindexpath:. Notice, this time, the app just paused because of a breakpoint and didn't crash.

You want to know exactly the details of the program crashing. Please click on the Continue button, or enter "C" after the prompt (LLDB) to continue with the execution. The program will continue to execute from the place where it was paused.

Nothing happened, you still paused at Tableview:cellforrowatindexpath: The breakpoint at this function. But in the Debug window it shows:

PROBLEMS[12540:F803] Array Contents: (One
,
two,
Three,
Four,
Five
)

This means Tableview:cellforrowatindexpath: there is no problem with the first execution because NSLog () executes after the breakpoint. So this app is a good way to create the first cell.

If you type the following to the debug prompt:

(LLDB) PO Indexpath

The debug window should be able to output the following:

(nsindexpath*) $ =0x06895680<nsindexpath0x6895680>2 indexes [0,1]

The above important parts are [0, 1]. This is the Nsindexpath object is section 0 and row 1. In other words, the TableView is now requesting a second line. From here we can speculate that the app did not have any problems creating the cell for the first time, just as there was no crash here.

A few more times this continue button. At a particular time, the program crashes and prints out the error message:

Problems[12540:f803]***terminating app due to uncaught exception ' nsrangeexception ',
reason: ' * * *-[__nsarraym Objectatindex:]: Index 5 beyond bounds [0.4] '
***firstthrow call stack: ... and so on
...

If you examine this Indexpath object, you can see:

(LLDB) PO indexpath
(nsindexpath*) $11 =0x06a8a6c0<nsindexpath0x6a8a6c0>2 indexes [0,5]

The section is still 0, but the row's index is 5. Note that the wrong message is also "index 5". Since the count starts at 0, when it comes to 5 it actually means it's already 6. But there are only 5 items here. Obviously this tableview thinks there are actually more lines here.

So the prisoner is the following method:

-(Nsinteger) TableView: (uitableview*) TableView numberofrowsinsection: (nsinteger) Section
{
return6;
}

This method should actually be written like this:

-(Nsinteger) TableView: (uitableview*) TableView numberofrowsinsection: (nsinteger) Section
{
return[list Count];
}

Remove the breakpoint or invalidate the breakpoint, and then run the program again. Finally the tableview showed up, and there was no collapse.

Note: This "po" command is useful for checking your objects. You can use this command when the program pauses in the debugger, or when you set a breakpoint, or when you crash. You need to be sure that this method is currently highlighted in the call stack, otherwise the debugger will not find the variable.
You can also see the variables on the left side of the debug window, but even if you see them it's not very convenient to know the details:
I just said that there was no collapse of the phenomenon.

OK, now let's try sliding removal. This app has ended up in TableView:commitEditingStyle:forRowAtIndexPath:

The error message is:

Problems[18835:f803]***assertion failure In-[uitableview _endcellanimationswithcontext:],
/SourceCache/UIKit_ sim/uikit-1912.3/uitableview.m:1046

This error looks like it came from Uikit, not from the app code. Enter "C" several times to let the system throw an exception so that you can get more useful information:

Terminating app due to uncaught exception ' nsinternalinconsistencyexception ',
reason: ' Invalid update:invalid Number of rows in section 0. The number of rows contained in a existing section after the
update (5) must is equal to the number of
rows conta ined in this section before the update (5), plus or minus the number of
rows inserted or deleted from this section (0 Inserted, 1 deleted) and plus or minus the number of
rows moved into or out of this section (0 moved in, 0 moved out). '
***firstthrow Call stack: ...

It means that the app tells the TableView to delete the line, but forgets to remove it from the data source. So this table view doesn't seem to change much. Modify this method:

-(void) TableView: (uitableview*) TableView Commiteditingstyle: (uitableviewcelleditingstyle) EditingStyle Forrowatindexpath: (nsindexpath*) Indexpath
{
if (Editingstyle ==uitableviewcelleditingstyledelete)
{
[list removeObjectAtIndex:indexPath.row];
[TableView Deleterowsatindexpaths:[nsarray Arraywithobject:indexpath] Withrowanimation: Uitableviewrowanimationfade];
}

Well, it looks like it's working, and you finally have an app that won't break down.

Remember the following points:

If your app crashes, the first thing to do is find out where it collapsed and why it collapsed. Once you know these two points, fixing the crash is easy. The debugger can help you, but you need to know how to get him to help you.

Some crashes may be random, and this is also the hardest one, especially if you're using multithreading. But most of the time, you can try, and you'll find some fixed ways to get your program to crash.

You can figure out how to use the fewest steps to reduce the crash, so you'll find a good way to fix the bug (which means he won't). But if you are not sure that you will not reproduce the error, you will never be sure that your modifications have fixed the bug.

Secret:

1. If the crash is inside the MAIN.M, you can set the global exception breakpoint (Exception breakpoint).

2. You are not getting useful information in the state where the exception breakpoint is turned on. In this case, run the app a few more times, or enter the "PO $eax" command after the debugging prompts.

3. The general cause of most crashes and some bugs are missing or wrong connections in your xib or storyboard. These situations do not appear in the compilation error, so you generally do not know.

4. Do not ignore compilation warnings. If you have a compile warning, you may have something wrong. If you don't know why you're going to a compile warning, it's best to figure it out. These are safe practices!

5. Debugging on the device may be slightly different from the one on the simulator. These two environments are not exactly the same, and you will get different results.

For example, when you run a problematic program on iphone4, the first crash occurs when Nsarray initializes, because you lack a nil tag, not because it crashes when the app executes setlist:. So the above principle approach will help you find the root of the crash problem.

Don't forget the static analyzer tool, which will catch more bugs. If you are a beginner, you are recommended to open it. You can set up your project on the Build Settings screen:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.