Correctly debugging IPhone Apps on XCode - iphone

The line below has an error, can you see it with a naked eye?
NSString *title = [sender titleForState:UIControlStateNormal];
NSString *newText = [[NSString alloc] initWithFormat:"%# button pressed.", title];
When the Iphone simulator executes this line it crashes without any other information. I must be missing something about debugging. The console showed me nothing.
I was able to figure out the error, my question is not about that. My question is why I get a hard crash with no help from XCode. Without any clue it took me precious 5 minutes before I could realize my typo.
Any suggestions? Or when programming for IPhone I just need to be very careful with typos?
EDIT: I guess some people did not see it immediately like me. The error is the lack of '#' for the string constant. So now my question is why XCode/Simulator did not show me any kind of error message, just crashed without any clues. Am I missing something about debugging?

Objective-C does not strongly verify that the arguments you pass to messages are of the right type during compilation nor at runtime. It should gives you a warning though. Here you pass a c string instead of a NSString. Because NSString are objects (thus referenced using pointer), your method uses it as a pointer while you feed it with a simple string. You then probably try to access unaccessible memory blocks...

I think you miss a # before the "%# button pressed".
The correct one should be:
NSString *newText = [[NSString alloc] initWithFormat:#"%# button pressed.", title];
All the constant NSString should be #"SOMETHING HERE";

Check you compilation warnings. That's all you need. On the case you are showing, you will get a proper warning that will alert you that bad things might happen at that line.

I get the following Error when compiling your code:
error: cannot convert 'const char*' to 'NSString*' in argument passing
Not sure what you need to do to get it to show you that, I'm working in Obj-C++.
Try adding "-Wall" to your "OtherWarningFlags" under your target's build settings.

Related

What's a reliable way to make an iOS app crash?

I want to test my app's crash reporting out in the field by deliberately having it crash when the user performs a particular action that a real user is unlikely to do accidentally.
But what's a good reliable way of making the app crash that doesn't create a warning at compile time?
Edit: Note that many seemingly obvious answers to this question result in exceptions that get caught by Cocoa and thus don't result in the app crashing.
in Objective-C use C directly to cause a bad access
strcpy(0, "bla");
Note: while this works on any system I know -- in a future version of the C runtime OR the compiler this might not lead to a crash anymore. see Is null pointer dereference undefined behavior in Objective-C?)
(in swift you would have to bridge to objC to do this)
My current favourite:
assert(! "crashing on purpose to test <insert your reason here>");
A classic:
kill( getpid(), SIGABRT );
And some pr0n:
*(long*)0 = 0xB16B00B5;
All of them generate crashes captured by my crash reporting tool.
Since we all use Clang for iOS, this is fairly reliable:
__builtin_trap();
This has the benefit that it's designed for exactly this purpose, so it shouldn't generate any compiler warnings or errors.
How about a good old stack overflow :)
- (void)stackOverflow
{
[self stackOverflow];
}
abort(); causes abnormal termination… That is a crash.
Most popular one - unrecognised selector crash:
NSObject *object = [[NSObject alloc] init];
[object performSelector:#selector(asfd)];
Make sure you don't have -asdf method implemented in that class haha
Or index beyond bound exception:
NSArray * array = [NSArray array];
[array objectAtIndex:5];
And of course
kill( getpid(), SIGABRT );
I think in Swift you could easily throw a fatal error:
func foo() {
fatalError("crash!")
}
It is actually even intended to use this feature in case something goes wrong in order to make the app crash.
To avoid an if statement in a special case, you could use precondition, too. It's similar to assert, makes thus the intention (if wanted) pretty clear and is not removed in the final release as assert. It is used like precondition(myBoolean, "This is a helpful error message for debugging.").
Send a message to a deallocated object
exit(0);
(must... type... 30 characters)
You can also raise an exception:
[NSException raise:NSInternalInconsistencyException
format:#"I want to test app crashes!."];
Add a gesture recognizer to a view that recognizes a 10 finger tap (5 fingers for iPhone as 10 can get a bit crowded). The GR has a method attached to it that executes anyone of the previously mentioned surefire ways to get your app to crash. Most users are not going to lay 10 fingers down on your app, so you're safe from the general user accidentally causing the crash.
However you should be able to use something like Testflight or just deploying it to personal devices and test in the wild before ever submitting it to Apple. Having a forced crash could get your app rejected by Apple.
could try something like
NSArray* crashingArray = [NSArray arrayWithCapacity:1];
[crashingArray release];
should crash on an EXC_BAD_ACCESS (might need to release it a second time but normaly it should crash like this already)
I will go with:int raise(int sig);
To get more info >man raise
I would just kill the process normally:
kill(getpid(), SIGKILL);
So if you install a handler with signal you can also handle the crash, finishing to write opened files and these things.
I use
[self doesNotRecognizeSelector:_cmd];
When working with RubyMotion I use this:
n=Pointer.new ('c', 1)
n[1000] ='h'
Try this:
- (IBAction)Button:(id)sender
{
NSArray *array = [NSArray new];
NSLog(#"%#",[array objectAtIndex:8]);
}
a wrong NSLog statement will do it
NSLog(#"%#",1);

Declare as NSString but Xcode treat as NSdata & received incompatible passing

I'm trying to pass an NSString address object to a UILabel text, but I received a warning from XCode compiler about incompatible pointer, as shown in the screen shot below:
And from the screen shot below you can see that my address object is declared as a NSString.
I had try displaying my NSString with:
NSLog(#"%#",[[LocationData objectAtIndex:rowDisplay] address]);
and it works without any incompatible pointer error. Can anyone please help? I have done some researching but I still can't find any solution.
My address is an object which gets stored into an NSArray. The address format is the always the same, for example "542 W 112 St New York NY 10025".
Thanks.
This might be due to the fact that there already is a method/property called address in some of SDK classes, and it happens to return NSData *.
NSArray's objectAtIndex: returns an object of type id, and the compiler doesn't know that it's your custom class that has address defined to return NSString *. When it tries to match address selector, it takes the one from SDK, and not yours.
You can however cast the returned object to your class and have your method address called:
[(YourClass *)[LocationData objectAtIndex:rowDisplay] address];
You don't see the warning when outputting address to NSLog since %# format accepts both NSString * and NSData * (for classes other than NSString it actually outputs the result of description method, which returns NSString *).
there is many follow up questions with your question. However, you said, you can show with :
NSLog(#"%#",[[LocationData objectAtIndex:rowDisplay] address]);
so why not use this into UILabel :
self.displayAddress.text = [NSString stringWithFormat:#"%#"[[LocationData objectAtIndex:rowDisplay] address]];
PS: please don't use image for 1 line of code. i have to retype your code here...

try catch final is not catching exception

While on return to my app from getting phone number from address book, if by mistake i fetch some address or any thing except for phone number, the code in the try catch final gives the following but doesn't catch it, any idea is appreciated in advance:
*** -[CFDictionary length]: message sent to deallocated instance 0x6a4db70
Code:
strContact = (NSString *)phone; CFRelease(phone);
// NSLog(#"%#", strContact);
CFRelease(multi);
name = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSRegularExpression *regex = [[[NSRegularExpression alloc] initWithPattern:#"[a-zA-Z]" options:0 error:NULL] autorelease];
// Assuming you have some NSString myString.
NSUInteger matches = [regex numberOfMatchesInString:strContact options:0 range:NSMakeRange(0, [strContact length])];
OK, I think I understand your question now. It just took me a while.
So, I'm assuming you have Zombie Objects enabled (NSZombieEnabled) and you're wondering why you can't catch this error --sending a message to a zombie-- with a #try/#catch structure.
The short answer is NSZombies don't throw an Objective-C exception (one that you can catch this way). There wouldn't really be a point to that, since you wouldn't actually ship an app with NSZombieEnabled anyway. This is meant to be a debugging tool that you use at the debugger or within Instruments.
In your actual app, when you turn off NSZombie's, there still wouldn't be an exception to catch for this, since you would just get an EXE_BAD_ACCESS, which is a UNIX signal (not this type of an exception at this level).
The last point is that Objective-C exceptions (the type that you can #try/#catch) are not recommended for errors that you expect to be able to recover from. Or in other words, this is meant for fatal errors only, where you might do some cleanup and then allow your app to still crash.
The right way to do what you're trying to do is to actually test for different types or different values that you might except to get and then handle each case appropriately. You can do this using normal if/else conditionals or something of that sort. You can test values for nil if you need to and you can see if an object is of a particular class by using [someObj isKindOfClass:[MyExpectedClass class]]
I hope I understood the question correctly?
The problem is probably that you are releasing phone after assigning it to strContact, and then using strContact. The string object is probably getting deallocated when you release phone, so the pointer in strContact is no longer valid.
Are you sure that phone is a string? Looks like you're storing that object in a NSString* pointer 'strContact' that is probably not a string, hence the 'CFDictionary' in the error messages.
Looks like you have two problems to fix:
Getting the object of the right type.
Ensuring that you don't release objects before you need to. Try put the CFRelease after you've finished using the object!

Why does this code produce leaks?

A total Noob here positing for the first time on this forum.
I am writing my very first iPhone app. The code in question is to populate my UITableViewCell in the cellForRowAtIndexPath method.
When I run my app with Leaks Instrument turned on, the leaking part points to this part of my code. I have read about memory management on various books and postings on this site and per my knowledge the code below seems to conform to the required alloc/release coding guidlines. However, the Leaks tool still points to this particular section. I simply don't understand why.
Your help would be appreciated.
ModelClass * myModelData = [myTempDataArray objectAtIndex:[indexPath row]];
NSString * myURL = [[NSString alloc] initWithFormat:#"%#/images/%#", SERVER_URL, [myModelData publisherLogoFileName]];
NSURL * imageURL = [[NSURL alloc] initWithString:myURL];
[myURL release];
[[cell productImageM] setUrl:imageURL];
[imageURL release];
[[self objMan] manage:[cell productImageM]];
return cell;
The Leaks tool doesn't tell you where the object should have been released, it tells you where it was allocated.
That bit of code looks fine; you own myURL long enough to use it to create imageURL and then release it. You give imageURL to the cell's productImageM and then release it. You then do some manipulation on objects you don't own.
At a guess, the type of object returned by [cell productImageM] retains its Url property and subsequently leaks it.
You can try to use the Build & Analyze option from the Build menu. In general it should indicate the exact line of the leak.

iPhone SDK mem management issues - EXC_BAD_ACCESS

I've been staring at this same issue for a long time now, and would really appreciate any help or suggestions. I'm sure its something simple, but I can't seem to find it. In my app delegate I'm loading up a bunch of accessory objects (an object I created, which supports NSCopying) with the following code:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Accessories" ofType:#"plist"];
NSDictionary *accDict = [[NSDictionary alloc] initWithContentsOfFile:path];
self.colors = (NSArray *) [accDict objectForKey:#"Colors"];
self.exteriorAccessories = [self loadAccessoriesForMode:EXTERIOR_MODE withDictionary:accDict];
self.interiorAccessories = [self loadAccessoriesForMode:INTERIOR_MODE withDictionary:accDict];
[accDict release];
And this is the definition for the method its calling:
-(NSArray *)loadAccessoriesForMode:(NSString *)mode withDictionary:(NSDictionary *) dictionary
{
NSMutableArray *tempValues = [[NSMutableArray alloc] init];
for (NSDictionary *value in [dictionary objectForKey:mode])
{
Accessory *accessory = [[Accessory alloc] initWithDictionary:value];
[tempValues addObject:accessory];
[accessory release];
}
NSArray *returnArray = [[NSArray alloc] initWithArray:tempValues copyItems:YES];
[tempValues release];
[returnArray autorelease];
return returnArray;
}
When I get to the release for accDict I'm getting an EXC_BAD_ACCESS exception. If I take out the release of accessory inside the loop, everything is fine - but I'm leaking Accessory objects (which seems obv. to me - if I init it and I alloc it, its my job to release it).
When I step through this in the debugger, I'm seeing the init, copy and dealloc methods all fire on my Accessory object as expected. I can also post code for the Accessory object if you think it will help, but I think the problem is somewhere in this code.
I think I've found the cause, but I'll post it here so others can possibly benefit. It didn't really have anything to do with the code I posted. Rather the problem was inside of the Accessory object. I was setting things directly instead of calling the getters through self.
So this:
value = [dict objectForKey:#"myKey"];
Instead of this:
self.value = [dict objectForKey:#"myKey"];
Somehow this was causing me to have bad side effects on the NSDictionary itself (I thought that was not mutable, but it seems I was somehow messing things up). The only way I found this was to use the very helpful advice that I found on Cocoa With Love.
When I used the Print Description option in XCode, I was able to see that the NSDictionary somehow contained AccessoryValue objects - one of my custom objects that should NOT have been there since this was just loaded from a simple plist. Print Description can be found in XCode by hovering over the object to see its details (while the process is paused in the debugger) and clicking on the little up/down arrows right next to the triangle that expands into object details. For dictionaries, this will dump their entire contents to the console.
Please prefix this with an "I know nothing about objective C but":
It looks to me like you need to release the accessory items after you have copied them into the "returnArray", or maybe not specify "copyItems".
Run Clang on your code. It's a godsend. Clang rules! It will do Static Analysis of your code and tell you what you might be leaking. Great stuff.
I was battling with the Exc_Bad_Access issue for a day and finally found the answer. The issue was when I try to access one of the objects stored in an NSDictionary, the first time was OK but the second access the object turned to be nil even though the object counts in the dictionary remains the same. This strange behavior was due to releasing the object twice. Here is an example:
NSString* nstring=[[[NSString alloc]init]autorelease]
[AnNSDictonaryInstance setObject:nstring forKey:0];
...
[nstring release];
Notice that nstring was set autorelease then release again? It won't show problem right away unit you try to read the dictionary object the second times. I hope one day Apple's development team will be able to flag this as an violation while compiling.
I hope this post will help someone out.
Wayne of Campbell