I have an app which is periodically triggering a "Collection was mutated while being enumerated." exception. The frequency of the exception seems to vary across devices.
I have a breakpoint set on all exceptions thrown, but at the time the exception is thrown, the only stack I get is __NSFastEnumerationMutationHandler and the assembly code it calls. I get the memory address for the __NSArrayM that's being modified, but I can't figure out how to reverse-engineer this into the name or contents of the array. Typing image lookup --address 0x20087d10 (the address the exception prints) in lldb gives me no output. $r0 gives me the exception object, but both po [$r0 callStackSymbols] and po [$r0 callStackReturnAddresses] return nil.
I also can't figure out how to determine which section of code is triggering it; the contents of the application threads at the time the exception breakpoint is hit vary from incident to incident and do not include any obvious accesses to collection objects.
How do I figure out which collection is being mutated and triggering this exception?
It turns out I was overcomplicating things - one can reference memory addresses directly at the lldb command line, so po 0x20087d10 is sufficient to print the description of the object at 0x20087d10.
(Murphy's law of StackOverflow... when you finally give up and post it on StackOverflow, you finally figure it out yourself.)
Related
I have a method that I want to debug:
-(void)doAThingWithObject:(BaseDataObject *)dataObject //called VERY often
And I have an Xcode breakpoint inside this method which I want to only break on a certain subclass of BaseDataObject, so I add a breakpoint w/conditional to check for that class:
[dataObject isKindOfClass:[SubClassOfBaseDataObject class]]
However, doing so results in a parse error!
Stopped due to an error evaluating condition of breakpoint 11.1: "[dataObject isKindOfClass:[SubClassOfBaseDataObject class]]"
Couldn't parse conditional expression:
error: no known method '+class'; cast the message send to the method's return type
error: 1 errors parsing expression
I have made sure to import all classes in the file, but the debugger does not know what class I'm referencing in the conditional.
However, creating a temp variable of said Class inside the method before the breakpoint:
Class subClassCheck = [SubClassOfBaseDataObject class];
And updating the breakpoint conditional to reference the temp variable:
[dataObject isKindOfClass:subClassCheck]
Throws no errors.
I'm a bit of a novice when it comes to breakpoint conditionals, can someone explain why my first approach doesn't work?
One complication with debugging code that is based on big frameworks like Cocoa is that it is not practical for the compiler to emit or the debugger to consume every type and function in the whole closure of frameworks you include. So the compiler uses some heuristics to reduce the amount of debug information generated. It will emit type information only for types that you actually use, and function/ObjC method information where the method is defined (as opposed to declared in a header file.) There's another little subtlety that lldb will read the type information for methods out of the ObjC runtime, though this information is not complete, since it is meant for the runtime not for debuggers... So we sometimes seem to know things about ObjC methods that violate the previous rule.
Another important thing to note is that the calling conventions for functions that return something larger than a pointer (like NSMakeRect, etc) are such that if the debugger calls a function thinking it returns a pointer and it actually returns a bigger structure, that act will cause stack corruption in your program. If you are lucky you will crash right away when you continue, but if you are unlucky it will just change some data value and cause you to spend hours trying to chase down some funny behavior that is actually caused by the debugger. So the debugger will refuse to call functions whose return type it can't determine.
Anyway the error you got is because the debugger couldn't find debug information for the "+class" method on your object. That is not altogether surprising, since "class" is a method on NSObject and not your class. I'm not sure why we couldn't find it in the runtime, maybe because it is a class method? That's worth a bug. We obviously did get the type of isKindOfClass: from the runtime, or your workaround would have also failed.
In this case, since you actually know the return type of the class method, you can work around the debugger's lack of knowledge by explicitly casting it in your breakpoint expression. Casting a function return in the debugger's expression parser serves two purposes, one is the regular C language function, and the other is telling the debugger the return type of a function it wouldn't otherwise be able to figure out. A sort of short-hand prototype only for the return type.
So something like:
[dataObject isKindOfClass: (Class) [SubClassOfBaseDataObject class]]
should work without having to alter your code.
Note also, the breakpoint conditions are run using the same mechanism as the "expr" or "print" command. So the easiest way to experiment with breakpoint commands is to set an unconditional breakpoint, hit it, then go to the lldb console and play around with "print" till you get something that works.
I have been at this for days and asked this question a few days ago. I thought it's probably better to rephrase the question more clearly.
I have a series of UICollectionViewControllers inside a UINavigationController. Each time the user makes a selection, a new collection view is pushed on the screen and this collection view is responsible for fetching its content from an API.
Sometimes there's a crash at this point. The exceptions are different each time. Usually, it's one of the following:
*** Collection <NSConcreteMapTable: 0x1922cf60> was mutated while being enumerated.
OR
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSSetM: 0x203f26b0> was mutated while being enumerated.'
Sometimes it doesn't even throw an exception but the program will pause in an "empty thread" saying error: address doesn't contain a section that points to a section in a object file. Finally, this error is more consistently reproducible when an item is selected, the new view is pushed, and then the back button is quickly pressed as soon as it appears. But it can also happen without the back button being pushed.
I have attached my own uncaught exception handler to print out the stack trace. In all cases, none of my own classes are in the trace.
I have 'break on exception' set but it never stops anywhere meaningful - usually some nondescript assembly code that just reads trap or in UIApplicationMain.
So my question is: Where do I even begin debugging this?
I can post an example of a stack trace if it helps.
First, set an exception breakpoint in Xcode. It will stop before the exception is thrown, so you know who caused it.
What seems to be happening is that you have code
for (id object in somearray) ...
and while that loop is running, someone modifies somearray. That is fatal. You probably have to think about how you are writing your code. For example, you can just write
for (id object in [somearray copy]) ...
and then you can modify somearray as much as you like. Maybe your code just isn't thread safe and another thread modifies somearray while one thread is iterating through it.
My code is:
SCDownloadManagerView *downLoadMnger = [[SCDownloadManagerView alloc]init]
[self.vw_ownVw addSubview:downLoadMnger.view]
[self.vw_ownVw bringSubviewToFront:downLoadMnger.view]
I am getting this error on second line [self.vw_ownVw addSubview:downLoadMnger.view]
Please help me.
In my experience, what usually causes this error is when memory has been released prematurely. In this case, it is possible that your program is trying to use an array, but because it was not properly retained, the array was deallocated, and an NSString was allocated in the same spot. When your program tries to access the array, it sends the count message to where it thinks the array is, but because a string has been allocated there instead, the string gets the count message and this causes an error because strings don't respond to count.
The code you posted is not the cause of the problem, it is only the point at which this bug is manifesting. In order to find the cause, you need to review your memory management. Try running "Build & Analyze", the static analyser is very good at picking up obvious mistakes in memory management. Review parts of your code that deal with arrays, but keep in mind that the array in question could also be managed by another object outside of your code (such as a view or view controller) that you have released too early, etc.
I have this error going on in Xcode:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString _isDecompressing]: unrecognized selector sent to instance 0x71863b0'
I have quite a bit of code and classes so I don't know what would need to be posted to start looking at this issue. If someone could give me some direction on how to start fixing this, it would be much appreciated. p.s. if there is anything else that needs to be posted tell me and I'll edit.
When you have unrecognized selector send to instance error, you have to check if you declared and implemented the method that is pointed out by the error, in your case _isDecompressing. If everything is ok on your class (the method is declared and implemented) then have a look at the class type that is calling the method, in your case NSString most of the time the class is wrong.
So in order to point out your problem, you are trying to call a method _isDecompressing on NSString which doesn't exist. So make sure every object that calls this method is of your desired type and not NSString
A good way to find the line that is causing the crash is to enable exceptions breackpoints.
The most likely cause of this crash is that you are sending a message to a deallocated instance of an object - try running your app with NSZomie's enabled - see e.g. How do I set up NSZombieEnabled in Xcode 4?
What is going on is that the memory used by your object gets marked as unused when deallocated and some other object gets allocated in that place. This object, however, is of a different class, hence the does not recognize selector message.
As noted in the comments, the way sending messages to deallocated instances manifests itself varies:
The object is allocated somewhere in memory - on a page, which is split into parts by an allocator - e.g. malloc. If the underlying allocator already returned the page where the object was to the kernel, then the app will crash with no log (EXC_BAD_ACCESS).
If the object was released and the retain count reached 0 it was deallocated, meaning just marking the memory on the page as free for future use. If you hence try to send another message to that object, the runtime will notice that the object has no retain count, hence was deallocated and will case the message sent to deallocated instance exception.
If, however, the memory that your initial object occupied was taken by another object in between, there's no way for the runtime to know that there was once an object you intend to call a method on, hence the unrecognized selector exception, since the class which the object belongs to is part of the object structure - the isa pointer. Nothing else is (or can be) checked by the run-time. For the runtime, it's a valid request to send a message to an object, however, there's no such method on the new object.
This can be potentially dangerous if the new object responds to the same message which does something lethal in one class, since the method is actually called on the object if it is a valid method name!
Of course, there are other scenarios, e.g. the object will overwritten by other data, hence the isa pointer points to a non-existant class and a crash will occur just as in the first place, since the OS will try to dereference an address that is not valid in the context of your process.
In the debugger console, use 'bt' to get a backtrace, then disassemble the first address in the backtrace the is noticeably smaller than the other values... the small valued addresses are your code.
This is easy to hit if you pull an image name out of, say, a JSON dictionary and pass it straight into something that expects a UIImage; since the values aren't type checked, the compiler will miss the error and you'll get a runtime crash.
(Ask me how I know!)
it's my call stack window when i got "Invalid Pointer Operation" Error :
CalStack http://m8spy.com//PersonalFs/M8SPY_Images/CallStack_1.png
What is the reason for this error?
Thank you
You've attempted to release memory that the memory manager doesn't recognize as belonging to it.
The exception comes from an object's destructor, which indicates that you're attempting to free an object that has already been freed. Otherwise, you're calling Free on an variable that never had a valid object reference in the first place; heed compiler warnings about uninitialized variables.
Please, see item "FastMM" in this article. Though it says about memory leaks, it is really an introduction to debugging memory managers, which are used to find problems with dynamic memory - just like your case.