Objective-C, NSLocalizedString Random Crash - iphone

I am getting a strange crash due to NSLocalizedString. -[CFString retain]: message sent to deallocated instance 0x3c6ad0.
I am sure it's due to the NSLocalizedString as when I replace it with static string. everything goes fine. Here is the line where I got the error.
[_backButton setTitle:NSLocalizedString(sBackButtonDefaultTitle, nil) forState:UIControlStateNormal];
The sBackButtonDefaultTitle is "Back".
What makes it random is that it crashes sometimes and sometimes not.
EDIT:
The sBackButtonDefaultTitle is defined in a header file
#define sBackButtonDefaultTitle #"Back"
The header is, of course, imported into the .m file.
EDIT:
More details...this back button title is used application-wide. In almost every controller. However, the crash only happens AFTER pushing certain controller.
This controller contains a web view. If I tap the Back button, the controller is poped. No crash. But If I push any controller with the same "Back" button, the application crashing, giving the error I illustrated above.
It's really mysterious. I hope someone can help me fix it.

Surely because you're sBackButtonDefaultTitle has been deallocated when you call NSLocalizedString.
I guess it's an auto-released string.
You may want to check with NSZombie enabled, to be sure.
You may need to retain the sBackButtonDefaultTitle string, when you create it, and release it after the NSLocalizedString call.

This crash is due to sBackButtonDefaultTitle getting released before that point in your code. Check to make sure you are retaining it when you take possession of it and that you are releasing it after using it in that line of code.

A good reason of using NSLocalizedString is to support more than one languages for your app. NSLocalizedString usually goes well with string resource file so that the system can dynamically decide with language it should use to populate your UI. I never tried to use the NSLocalizedString as the way you do, but I can tell you use a string resource file will definitely work. Take a look at Apple's Guidelines for Internationalization
and string resource file
Hope it helps.

Related

An initWithCoder method does not return a value

I am trying to resolve an application crash on startup. I made some mods to my app and now it crashes with the following message:
This coder requires that replaced objects be returned from initWithCoder
I made very small changes and I haven't done anything outside of the norm other than introduce a few IBOutlets in two view controllers. They both need to share an NSMutableDictionary for configuration purposes. I can't figure this out as everything was working fine before. :( I'm not calling initWithCoder directly anywhere and there's nothing in the stack that indicates anything I've done wrong. It's hinting towards the nib changes I made which don't make any sense! Does anyone know how to resolve this?
Seconds after posting I removed the NSMutableDictionary custom object from my nib and the crash goes away. I guess I cannot load/instantiate dictionary objects from a nib? This is odd and frustrating. I'm trying to forego the need for a dependency injection framework (objection) which is why I'm trying to use the nib file to load and wire everything together.

How to work around/handle delegation EXC_BAD_ACCESS errors? Obj C

I'm coding a library (Obj-C for iPhone) that I want to package and sell, so I obviously need to work out any design kinks before listing it for sale. I am also utilizing this library to help me develop another app.
My library is heavily built on task delegation. The primary function I have is to launch a (potentially) long-running process, and when it's done, I call a Delegate Protocol method in the class's delegate.
An additional complicating factor here is that I will often schedule this task to fire off every 30 seconds or so. Usually, I do this with [self performSelector:#selector(someMethod:) withObject:nil afterDelay:30] rather than using an NSTimer. Then, when the delegate method successfully returns, I process the returned data and trigger the method to fire in another 30 seconds. This gives me 30 seconds BETWEEN method calls, rather than 30 seconds FROM THE START OF ONE CALL TO THE NEXT. (This is mainly just in case the call ever takes more than 30 seconds, which shouldn't happen.)
The error that I'm catching is that sometimes, the Delegate callback method is failing with an EXC_BAD_ACCESS error. Based upon my investigation, it appears that the delegate of my class library has disappeared (been released/dealloced) since the long-running process was initiated. Thus, when it calls [[self Delegate] doSomeDelegateMethod], it's accessing a released object.
I tried first checking [[self Delegate] respondsToSelector:#selector(doSomeDelegateMethod)], but even that access apparently also throws the EXC_BAD_ACCESS.
It doesn't yet seem that checking for [self Delegate] == nil is the right way to go, either.
One way I think I have solved the problem, in this specific instance, is when the view controller that instantiates my object is disappearing (and therefore on its way to the garbage dump), I call [NSObject cancelPreviousPerformRequestsWithTarget:self]. This apparently fixes the problem. (Does this "fix" also indicate that my object "knows" about the call to come and keeps itself in memory until it can successfully, desperately, fire off its final shot?)
This appears to put a band-aid on a bullet wound. Yes, it appears to stop my app from breaking this time, but my gut tells me that this is a poor solution.
I've also considered setting the custom object to nil in my viewWillDisappear:animated: method, which is probably the correct coding pattern, but it doesn't seem right that the customer has to be so precise in handling my objects.
What's really bugging me, though, is that I haven't yet found a way, as a library developer, to "box in" my code so that it won't throw an exception for the user if they don't do just the right things. Basically, I'd like a way to have my object:
Get a request.
Go look for the answer.
Find the answer.
Try to return the answer.
Realize that there's nothing on the other end.
Give up and die on its own. (OK, so "die on its own" probably won't happen, but you get the point.)
One interesting side point:
A main reason I have for preventing this type of error from occurring is this:
I did the following steps:
Built my library's .h/.m files.
Generated my library's .a output file.
Imported my library's .a/.h files into another project.
Had the error described above.
Got to peruse the code from one of the .m files that SHOULD have been hidden inside the .a file.
Am I missing something here? Am I really risking exposing my entire source code if it ever throws an error for a client? (This is just a side issue, but I'm fairly concerned here!)
Thanks for any help you can provide to help me be a better programmer!
---EDIT---
I have found another reason why this is important. In another view controller, where I am using this library, I implemented the NSTimer strategy. If the view is popped from the navigation stack (i.e., in the viewWillDisappear:animated: method), I invalidate said timer. So, no more calls will go to my library after the view disappears.
Here's the rub: what if the view disappears IN THE MIDDLE of the long-running call? Yes, it's tricky and unlikely to do, but I just had it happen on the simulator. In particular, THIS is why I'm looking for a workaround to let my code realize "hey, there's nothing on the other end of this pipe" and then fail gracefully. Anyone?
Thanks!
There are several approaches to this problem:
The traditional delegate approach (UITableViewDelegate) makes it a requirement to clear yourself as delegate before going away. This is traditionally done in dealloc of the delegate with otherObject.delegate = nil. Failure to do so is a programming error. That's basically what you're seeing. This is the common pattern when the delegate and the delegator have basically the same lifespan.
Another approach is how NSURLConnection handles it: retain your delegate until you're done. The key to this working well is that NSURLConnection has a lifespan of its own, so the retain loop will work itself out automatically. UITableView could not retain its delegate because this would almost always create a permanent retain loop. If your object lives for a while and then goes away, then this makes sense. Typically here the delegate has a much shorter lifespan than the delegator, so the retain loop doesn't hurt anything.
Any object that calls performSelector:withObject:afterDelay: should always call cancelPreviousPerformRequestsWithTarget:self in its own dealloc. This has nothing to do with your delegate, though. It should be self-contained to the object itself. (I don't know why I keep thinking this is true, and then proving to myself again that it isn't. When you call performSelector:...afterDelay:, you are retained, so you can't deallocate before it fires. My SIDE NOTE, while true, isn't relevant here.)
SIDE NOTE cancelPrevious... is really expensive in my experience. If you have to call cancelPrvious... very often, I recommend keeping your own one-shot NSTimer and just resetting it when it fires to get the same effect. performSelector:withObject:afterDelay: is just a wrapper around a one-shot timer.
I'm answering myself because the page warned me to not have extended discussions in the comments... :)
OK, so it appears that part of my answer is that [self performSelector:withObject:afterDelay:] automatically retains my object until it gets to "fire that shot", at which point I'm guessing the view controller dies.
So, now it makes sense why my custom class is trying to access a released object when it tries to return its answer to its delegate, which is an __unsafe_unretained object, meaning that it can die at will (I think).
What I'd like now is a way to prevent this from causing an error. In .NET, I've got all sorts of error handling options to do this, but I'm unable to think of a fail-safe "bail out" here.
I've tried [[self Delegate] isKindOfClass:..., but can't be sure what kind of class the delegate will be, so it won't work.
I've also tried [[self Delegate] respondsToSelector:#selector(...)]. I'm not sure why this fails, but I get the EXC_BAD_ACCESS here, too.
What I don't want is my customers to be able to crash my product with such a simple, innocent mistake.
As an aside, does anyone know why this sort of failure gives me such easy access to the contents of the .m file that should be hidden inside my .a file? Did I build my library incorrectly?
Thanks!
Try setting Delegates to nil in dealloc.
example:
self.fetchedResultsController.delegate = nil;
I've seen this problem a lot lately and usually fix the problem. Even though delegates are supposed to be weak references, sometimes some private implementation is using them as well.
If I release, I get bad access, if I retain, I leak
That's where I had a similar problem.
Edit: When using ARC, you can still override dealloc for cleanup, you just can't call [super dealloc] or release anything.

iOS UITableView random crash

I have a problem I can't locate clearly, maybe you can help me...
I have an iPad project, based on UINavigationController, most (but not all) of controllers inside are instances of UITableViewController, and everything works well...
Everything excepting that my application crashes randomly, sometimes after 10 minutes of use, sometimes after only 10 seconds...
It never crashes on the same view, never at the same time, making that difficult to reproduce.
In addition, it only seams to happen on device, I've never got this crash in the simulator.
The debugger doesn't help me very much, here is what it says:
-[UITableView autorelease]: message sent to deallocated instance 0x8e9800
And here is the call stack:
http://i.stack.imgur.com/JSCHx.png
Any idea ?
Thanks (and sorry for my english)
You're overreleasing a UITableView somewhere in your code. Are you calling release or autorelease on the UITableView inside one of your UITableViewControllers? You should only release objects that you 'own'. You get to own an object by using methods beginning with alloc, new, copy, or retain.
Please read the cocoa memory management guidelines for more info.
Useful links:
http://www.cocoadev.com/index.pl?MemoryManagement
http://www.cocoadev.com/index.pl?RulesOfThumb
At some point you are either releasing a UITableView instance that you do not own or you are failing to retain one at some point where you keep a reference to it (e.g. you store it in an ivar or a property declared assign rather than retain).
I have written about how to debug things like this on my blog:
http://loufranco.com/blog/files/Understanding-EXC_BAD_ACCESS.html
Basically, try these three things first:
Do a Build an Analyze and fix everything you see
Turn on Zombies, run your code -- it will tell you if you talk to dealloced objects
If that fails, try Debug Malloc, but that's way harder.
I apologize, after re-reading all my source code, I found ONE ViewController (I have around 20 ViewController), where I released an Outlet, in ViewDidUnload.
The reason that it crashed randomly is that I didn't understood well the mechanism of ViewDidUnload, it is called to release views (but not objects of controllers) when memory is low and view is not visible (ex: First ViewController of a NavigationController), and the views are re-loaded when the ViewController become visible again...
In simulator, memory is rarely an issue so ViewDidUnload is almost never called...
Problem fixed, thank you everyone for your help
To help with making sense of the trace, see iOS Debugging Magic (Technical Note TN2239) and Understanding and Analyzing iPhone OS Application Crash Reports (Technical Note TN2151).
Jeff

iPhone Simulator crash with WebPreferences in the thread list

Apple Developer Reference Library has a class reference for WebPreferences
I've searched SO, Dev Forums and Googled without any relevant results.
EXC_BAD_ACCESS signal is generated.
I can't find a crash report.. its happening on the simulator. The debugger is called, and I don't get a crash report.
EDIT
This is triggered when tapping a UITextField, leaving a UITextField or if a UITextField is set as first responder when loading a view (push on by a navigation controller).
It is not easy to reproduce. I can go for a hundred app-launch/debug cycles before it will happen again. And then it might happen 3 times in 5 launches.
I do have a thread list in the debugger that shows several references to WebPreferences.
You're on the right track if you are using NSZombie. EXEC_BAD_ACCESS is caused by accessing released objects.
It is normal for EXEC_BAD_ACCESS to "crash" in code paths that do not belong to you. Most likely your code created the over-released object.
The key part of using NSZombie is running the malloc_history on the command line. You'll get the call stack showing where the over-released object originated from. For example:
alt text http://static.benford.name/malloc_history.png
The screenshot shows my app crashing at [NSString stringByTrimmingCharactersInSet:] but that is certainly not who caused the crash.
I technique I use is to look at the earliest code path that you own. Most of the time the mistake lies there.
In this case, the object originated from the class [JTServiceHttpRequest requestFinished], in which I was not properly retaining a object.
If all else fails, go through all your code paths listed and verify your use of proper memory management rules.
My bet is that WebPreferences and UITextField behavior have nothing to do with the crash.
For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.
This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.
It especially helps in background threads when the Debugger sometimes craps out on any useful information.
VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
NSLog(#"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");
If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.
The fact that it's crashing in _integerValueForKey: makes me strongly suspect that it's crashing on an over-released NSNumber. Over-releasing an NSNumber can lead to such bizarre crashes that it's almost humorous. Here's what happens:
You create a NSNumber for the integer "2"
The NSNumber class caches the NSNumber
You over-release it
The NSNumber class's cache now points to bad memory
Some completely unrelated piece of code creates an NSNumber for the integer "2"
The NSNumber class looks it up in its cache, and....
Bang
If you're running Snow Leopard, hit Cmd-Shift-A to let the analyzer look for memory management problems. Otherwise, go hunting in your use of NSNumbers.
agreed with previous responders about NSZombie. Most often this thing happens when you use your class as a delegate of a UITextView(or any else class) and also refer it in IBOutlet variable. when you leave your viewcontroller - it become deallocated. but if you didn't release IBOutlet variable in - (void) dealloc method - UITextView will still send calls to released delegate (your view controller).
Sounds more like an Auto-Release bug. You might be releasing something you don't "own" and the NSAutoRelease pool is running and trying to clean up something that was already released?
Did you release something you didn't "alloc"? For example, you wouldn't do:
NSString *test = #"testing";
[test release];
As this will cause the crash to happen when the Auto Release pool runs and attempts to release the NSString.

How do I debug an 'unrecognized selector sent to instance' problem?

I have the following code in a view controller that (in all other respects) seems to be working fine:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ProblemViewController *problemViewController = [[ProblemViewController alloc] initWithNibName:#"ProblemViewController" bundle:nil];
problemViewController.problem = (Problem*)[self.problems objectAtIndex:indexPath.row];
[self.navigationController pushViewController:problemViewController];
[problemViewController release];
}
When I run through this function, however, I am getting the following error:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[ProblemViewController initWithNibName:bundle:]: unrecognized selector sent to instance 0x57ca80'
I'm at a loss as to what could be causing it, so my question is: How should I best go about debugging this problem? Is there anything obvious I should be checking?
Check the base class for ProblemViewController. I'm betting you're not inheriting from UIViewController, which includes the initWithNibName:bundle: method.
There are a bunch of answers to this question that all basically say the same, correct, thing: Somehow, the method you're trying to call doesn't actually exist.
BUT, if you're beating your head against the wall (like I was for an hour today), make sure you try cleaning your project in xcode first. Sometimes, I don't know why, xcode can get into a bad state and will not properly compile your project into the simulator. It will tell you the build is successful, but when deployed to the simulator, you start seeing runtime errors as though only half your changes were picked up. So yeah, that happens.
You likely don't have that method implemented in your ProblemViewController. Unrecognized selector is, as far as I know, just that there's no method defined in this class's interface that has that signature.
Try declaring it in your interface like this:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
I thought I better post something on this issue. I fought it for a couple of days. There are a lot of blogs on this, but none of them solved the problem. Some suggested that it had to do with pushViewController needing to specify animated:YES. It worked for some, but that didn't work for the issue I was having. I was using a button to access another view rather than selecting a row from a table view, but was getting the same unrecognized selector error. I was also using a navigation controller so I thought it had something to do with that, but it didn't. Finally, I decided to comment out lines of code till the message went away. After commenting out the method that was causing the problem, the error message still came up in the console. That is when I realized the problem was not with my source code. One site recommended performing a clean and rebuild. I tried all that and still the problem persisted. I then looked at my XIB file using Interface Builder to see what methods(Received Actions) displayed on the File Owner. There was the problem. Not only did the offending method show up once on the File Owner, but it displayed twice. Don't know how to display the image of this here. Anyway, I deleted the methods (Received Actions) with the same name that appeared on the File Owner. I performed a clean and rebuild to see if the error went away and it did. I then uncommented the source I thought was bad and built the project again and the call to the new view was successful.
I will share my experience with the same error code. It is possible to make a mistake my assigning a the object to the wrong target. For example, if you have some UILabel property and you have accidentally assigned the string constant directly to self.myUILabelProperty = #"ups" then you property will become object of type NSString instead of being UILabel, so you loose all the UILabel methods. After that mistake, if you try to use UIlabel methods on the property in code you will get this error message.