IOS/Objective-C: Trace precise place of exception - ios

I am getting an exception when I load a view controller. The error message suggests the program is trying to send length to NSNull as follows:
[NSNull length]: unrecognized selector sent to instance 0x3b1cda70
(lldb)
The last log statement prior to the exception is at the end of a method and from what I can tell although a value is null that it returns, this method is not the issue.
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(NSInteger)component
{
NSLog(#"in titleforrow");
NSLog(#"about to return firsts[row]%#",firsts[row]);//this is null
return firsts[row];
}
This suggests the problem is in some other method. So I have put log statements before every use of length in the view controller class (all of which are in a save method that should not be called on loading) and, in fact, none of these log statements appear in the log suggesting that length is never sent.
Nonetheless, the program is throwing the exception. I tried adding an exception breakpoint as suggested in this answer
While it lets me view the threads, I still can't figure out what is throwing the exception.
Would appreciate any suggestions on where to go from here.

Related

Odd error (UITextSelectionView) while calling a method?

I'm facing a problem while calling a method and I don't know how figure it out.
Basically, during the main menu, I want to call a SKNode showing a tutorial part. The code is the following:
- (void)didMoveToView:(SKView *)view
{
...
if ([[GameData sharedData] openingTutorial]) { // Checks if the menu needs the tutorial
[_tutorialObj performSelector:#selector(runTutorialWithStep:)
withObject:[NSNumber numberWithInt:8]
afterDelay:3.0
];
}
}
When the method didMoveToView: is called (even before waiting the 3 seconds for the runTutorialWithStep:), I got this error:
[UITextSelectionView name]: unrecognized selector sent to instance 0x1576e6c0
2014-10-14 11:01:19.430 [406:130379] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITextSelectionView name]: unrecognized selector sent to instance 0x1576e6c0'
The odd thing is that in the previous class I use the same tutorial's action in the didMoveToView: and it's working right. But...
Thing is getting more strange here!!!
If I use an intermediary method for this calls:
- (void)didMoveToView:(SKView *)view
{
...
[self performSelector:#selector(intermediaryMethod)
withObject:nil
afterDelay:3.0
];
}
- (void)intermediaryMethod
{
[_tutorialObj performSelector:#selector(runTutorialWithStep:)
withObject:[NSNumber numberWithInt:8]
afterDelay:0.0
];
}
Everything works without issues. I don't want to avoid the problem but solve this. Any advices?
The error says it all. You try to send a 'name' message to object that doesn't implement it (UITextSelectionView). Since your second take works the cause is either in
[[GameData sharedData] openingTutorial]
method or before this call. Search for objects calling 'name' and check if it's the UITextSelectionView in question.
That or maybe you have weak reference to general view object and during those three seconds before calling runTutorialWithStep you reassign it to object that implements 'name' message.
Ok, I solved the problem.
I added a breakpoint for every exception and I realized that the issues was due to an other class (the only one with UITextView).
Basically I removed the text field from its parent ([self removeFromParent]) by my own once I did not need it anymore.
I suppose the error was fired during the deallocation 'cause the program can't find the UITextView. I managed to remove this statement and everything works right.
Actually I still have doubts because I don't understand why this exception is throw only if I call the [_tutorialObj runTutorialWithStep:] method.
I found the way to fix it.
UITextView *textView = .....
textView.selectable = NO;

Message forwarding does not work on particular messages

I've found out that some Obj-C messages do no get properly forwaded when an object does not explicity handle them.
Here's an example:
Create a new UITableView like:
UITableView *v ? [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) style:UITableViewStylePlain];
Then create an instance of an NSObject with the following methods:
(A) - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"tableView:numberOfRowsInSection:");
return 1;
}
(B) - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"tableView:cellForRowAtIndexPath:");
return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSLog(#"methodSignatureForSelector: %#", NSStringFromSelector(aSelector));
return [NSMethodSignature signatureWithObjCTypes:"##:#"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(#"forwardInvocation: %#", NSStringFromSelector(anInvocation.selector));
}
Let's call this object ds and set it as the dataSource for the table view:
v.dataSource = ds;
And when you run your code you should see that both methods (A) and (B) get called by the table view in the expected order:
LOG: tableView:numberOfRowsInSection:
LOG: tableView:cellForRowAtIndexPath:
Now it will get interesting, try removing (or just change the name of) method (A), and you should get:
LOG: methodSignatureForSelector: tableView:numberOfRowsInSection:
LOG: forwardInvocation: tableView:numberOfRowsInSection:
Which is fine because the table view tries to call the tableView:numberOfRowsInSection: and since the method is not there, the message gets forwarded to forwardInvocation:
Apparently, this is the default behavior for all messages that reference a method that is not defined at an object.
However try restoring the (A) method and removing (B), and you should get:
LOG: tableView:numberOfRowsInSection:
... and an error telling you that tableView:cellForRowAtIndexPath: was called but didn't return a cell.
For some reason tableView:cellForRowAtIndexPath: gets called (or not) but without following the expected forwardInvocation: pathway.
Is this a bug?
Am I doing something wrong?
Are there some messages that explicitly do not get forwarded?
Even though respondsToSelector: should return FALSE to both functions, somewhere in the implementation sends the message for cellForRowAtIndexPath: to some other object and breaks the default expected behavior...
What I've learned is basically:
If you are planning to respond to a message in your object, even through some implementation of forwardInvocation:, you should ALWAYS overload the respondsToSelector: method and return TRUE for all messages you plan to implement in your object.
The following is my hypothesis based on some experimentation:
The difference is that the table view doesn't attempt to call cellForRowAtIndexPath if it isn't defined, whereas it does attempt to call numberOfRowsInSection. Therefore, you only see the forwarding mechanism with numberOfRowsInSection. The error telling you a cell wasn't returned is just poorly worded and slightly misleading.
I came to this conclusion by observing that, if the data source doesn't define cellForRowAtIndexPath, the implementation on UITableViewController gets called instead. So, the table view is clearly checking for existence. Therefore, it stands to reason that what happens is something like this:
Call on dataSource if defined
Otherwise, call on UITableViewController if defined
Otherwise, don't call anything
If (3) is reached, then somewhere downstream an error is thrown because the table view's internal cell variable is nil.
The runtime system forwards messages only if it's unable to find a match for the given selector in the target class or any of its ancestors. However if the runtime system's search for the selector succeeds, it then invokes the method's implementation directly -- no need for forwarding.
By the way, if the lookup fails, the runtime system calls forwardingTargetForSelector: first; it then calls forwardInvocation: only if forwardingTargetForSelector: returns either nil or self.

How to lookup code relating to variable memory address from Xcode IOS crash?

I have a weird crash here that I believe is stemming from an external library, but I'm having trouble tracking down the stack trace, and not sure how to use Xcode's memory exploration tools.
This is what I'm getting in the console when it crashes:
*** Terminating app due to uncaught exception 'NSGenericException',
reason: '*** Collection <__NSArrayM: 0x208c5d00> was mutated while being enumerated.'
*** First throw call stack:
(0x37ae32a3 0x35e0d97f 0x37ae2d85 0x7c57d 0x728b1 0x6b865 0x34d9f11f 0x34d9e4b7 0x34da31bd 0x37ab6f3b 0x37a29ebd 0x37a29d49 0x368602eb 0x38ffb2f9 0x643d1 0x64358)
libc++abi.dylib: terminate called throwing an exception
I'm using MKNetworkKits UIImageView setImageFromURL additions to supply images to a UITableViewCell imageview (a custom imageview object, not the default imageview that comes with UITableViewCell). When I remove this setImageFromURL call, I don't get any crashes.
I tried using dwarfdump and atos on the command line but none of the addresses in the crash above link to any specific function addresses.
I have scoured for places where forin enumeration loops are being performed, but can't seem to find any that actually mutate data. Clearly I'm overlooking something..
Any advice/tips/help here would be super appreciated..
EDIT: Thanks so far for the comments. Any tips on how to utilize the memory address to trace down actual lines of codes would be helpful - can Xcode do some of what Visual Studio debugger can do, with respect to code & memory inspection?
Steps :
Run application and check on which ViewController the app is crashing with this exception.
Once you find the ViewController check entire code for that controller for NSArray.
Check related delegate you are calling from that controller, if any.
Check all custom subclasses you are using in that controller.
From the description it seems like you are enumerating an NSArray and checking values of it. Once you find the value you want to change those value. So, you are creating a mutableCopy of NSArray and trying to change inside the current running enumeration.
Solution :
Create NSMutableArray outside enumeration. While enumerating NSArray the objectAtIndex: will remain same for both array when you find the value you want to change. Change it inside mutable copy(created outside). [Bad for memory]
Create NSMutableArray outside enumeration of current NSArray. Then enumerate NSMutableArray. [Enumeration Will be Slower as you are enumerating mutable copy]
Hunt the Bug down!
This maybe useful for you:
replace your main.m file code with this (for ARC):
int main(int argc, char *argv[])
{
#autoreleasepool {
int retVal = -1;
#try {
retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([FRAppDelegate class]));
}
#catch (NSException* exception) {
NSLog(#"Uncaught exception: %#", exception.description);
NSLog(#"Stack trace: %#", [exception callStackSymbols]);
}
return retVal;
}
}
I suggest, starting simple, somewhere, you are altering an array, probably an NSMutableArray inside a loop. This can be easily fixed by using temporary store, them remove the temp objects AFTER the loop completes.
If this does not help, set an exception break point to break on all exceptions. This will likely stop on the line of code causing the error.
This helped:
https://stackoverflow.com/a/12266856/420594
Editing the Debug scheme and switching from LLDB to GDB helped a great deal - the crash points directly to a line of my own code now.

How can I find the sender of an unrecognized selector in LLDB?

I'm getting an "unrecognized selector" error that is confusing me.
I know that I can "po" the pointer in LLDB to find out about the receiver. But is there a way to deduce which object is sending this message?
Thanks!
The command bt in the debugger will show you a backtrace (stacktrace), which should give you the class that initiated the message somewhere in that output.
Backtrace doesn't always help if you're dealing with multiple threads - you end up with the backtrace of the exception handler on the main thread which isn't necessarily the one that cause the error.
However, since you know that the particular selector doesn't exist for a particular class, you can cheat a little by using a category to add the selector to the class, then just stick a breakpoint on it.
For example, for this error:
-[__NSCFDictionary isEqualToString:]: unrecognized selector sent to instance 0x10004fb0
we know that something's trying to call "NSDictionary" with "isEqualToString". So, add this at the end of any file you like, outside any other "#implementation" blocks:
#implementation NSDictionary(debug)
- (BOOL)isEqualToString:(NSString*)theString {
return FALSE;
}
#end
Stick a breakpoint on this, re-run your code and you will get a genuine stack trace. Don't forget to remove the category once you've found the error!

[NSCFArray row]: unrecognized selector sent to instance 0x3953a20

I get a crash with this console message:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '*** -[NSCFArray row]:
unrecognized selector sent to instance 0x3953a20'
This happens when I scroll trough a table that gets its data from an array of dictionaries.
Look in the crash log's stack trace to see where exactly this call is happening.
If the variable you're sending -row to isn't actually typed as an NSArray, it's likely that you've failed to follow the memory management rules for that variable. These same symptoms are very commonly caused by that. Something that responds to -row could have existed at one point, been deallocated because you didn't -retain it, and then an NSArray was later allocated in that spot.
Run a "Build & Analyze," and re-re-review the memory management guidelines until you know them in your sleep.
Looks like you are sending a row message to an NSArray. There is no row method defined in the NSArray class. If you are using a table, my guess is you want to send "row" to the indexPath parameter to get the position and then get the data at that position in your data array:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// first row will be like 0
NSUInteger row = [indexPath row];
// get the same row position in your data array
id data = [YOUR_DATA_ARRAY objectAtIndex:row];
}
That will give you the numeric position of the row. One caveat: "row" is not part of the base NSIndexPath class. It's added as a category in UIKit.

Resources