Why would an iOS app act differently on two different devices? - ios

If you were developing an iOS app using Xcode and were testing it on different devices, is there any reason as to why it would act differently given that the devices are the same model and have the same software version?
For example, I'd recently been working on an app. A clean build of that app were put on two different iPhone5s. (Same software version)
However while running on one of the phones I'd get a memory deallocation error similar to the following:
*** -[CFString release]: message sent to deallocated instance
Where as on the other device, no such error would appear.
Are there any obvious reasons as to why this might be the case for any app?

Your code probably isn't acting differently on the different devices. The problem most likely exists on all your devices, but will only cause a problem in certain circumstances. You can not predict when objects in the autorelease pool get deallocated, but you can be sure that it's not always going to be with the same timing. Running on different devices, timing of the draining of the autorelease pool can be different due to other background processes, memory usage, etc.
The deallocation error that you are seeing is most likely due to a release call you do on an autoreleased object, that you did not call retain on yourself. Check you code for objects that your create without an init call, but where you are calling release on. Also, try running your code in Instruments with enable Zombie Objects to see if you can find the NSString object that is causing the problem.

Your String is being under-retained or released.This problem occurs when you are trying to access the deallocated object. May be your string get released before you accessing it.I've face this problem.
Run your app in Instruments using the Zombies template. That will show you what object had the memory issue, and will let you see the entire retain/release history of that object. That should point you in the right direction.
For Zombies enable
option+cmnd+r
and then select Enable Zombie Objects and then Run.

Related

Avoiding fatal crashes

I have been debugging an app for a while and am ready to upload it to app store. However, I still get occasional crashes that are hard to duplicate or debug--for example when pushing buttons in weird sequences. If I write these sequences down, I can debug but inevitably it happens when I haven't written it down and these can be difficult to replicate.
I know I need to just call it a day and leave future bug fixes for next release. I have removed all the abort() statements I had in testing. However, occasionally rather than getting a crash that lets you just close the app and reopen, I get one that makes it impossible to open the app without reinstalling. For example, this just happened, with a
'NSGenericException', reason: '*** Collection <__NSCFSet: 0x174a41b90> was mutated while being enumerated.'
This resulted from switching VCs during a background sync to the cloud.
I can live with crashes where you just need to reopen but not with ones that render the app unusable. Are there any guidelines for types of crashes to help you focus on the really fatal ones?
Or is there anything you can do to keep crashes from bricking app?
You should just fix this problem. Since you have this crashlog with that error message, you know which method can raise the problem, so you've got a good head start on fixing it, even if the problem manifests itself inconsistently and/or in a variety of ways.
But the occasional crash may not seem like too much of an inconvenience to you, but it is the quickest way to 1-star reviews and unhappy customers. I suspect you will quickly regret distributing an app with known, easily reproduced crashes.
But, a few observations:
It sounds like your background update process is mutating your model objects used by the main thread.
If possible, I'd suggest being careful to simply do not change any of your model objects in the background thread, but rather populate a local variable and when you're ready to update the UI accordingly, dispatch both the model update and UI refresh to the main thread.
If you cannot do this for some reason, you have to synchronize all interaction of model updates with some mechanism such as locks, GCD serial queue, reader-writer model, etc. This is slightly more complicated approach, but can be done.
I would advise temporarily editing your target's "scheme" and turn on the thread sanitizer:
It may possibly help you identify and more easily reproduce these sorts of problems. And the more easily you can reproduce the problem, the more easily you will be able to fix the issue.
You say:
Or is there anything you can do to keep crashes from bricking app?
It sounds like the "save" operation is somehow leaving the results in persistent storage in an internally inconsistent manner. Any one of the following would help, though I'd suggest you do all three if possible):
At the risk of repeating myself, fix the crash (it's always better to eliminate the source of the problem than try to program around manifestations of the problem);
Depending upon how you're saving your results, you may be able to employ an atomic save operation, so that if it fails half way, it won't leave it in an inconsistent state; we can't advise how you should do that without seeing code snippet illustrating how you're saving the results, but it's often an option;
Make sure that, if the "load" process that reads the persistent storage can fail, that it does so gracefully, rather than crashing; see if you can get it in this state where the app is failing during start-up, and then carefully debug what's going on in the start-up process that is causing the app to fail with this particular set of data in persistent storage. In the "devices" section, there is an option to download the data associated with an app, so you can carefully diagnose what's going on.

Alternative to NSZombie

I'm trying to debug an EXC_BAD_ACCESS crash using NSZombie. My app creates lots of large objects though and with NSZombie enabled they aren't getting released causing the app to crash in seconds. This means I can't even cause the EXC_BAD_ACCESS crash before the app crashes due to low memory.
Is there an alternative? Can I enable NSZombie on specific file instead of the entire project? How else could I debug this crash (I know it's caused by UIGestureRecognizer but I use them a lot so it doesn't narrow down the issue significantly).
Thanks.
Edit:
Thanks for the advice. I think I may have solved the issue and will report back after more testing.
Edit 2: Sovled the issue myself but selected the answer which seems like it would be a good solution to any similar issues in the future.
All I can think of is implementing it manually; create a proxy container that holds an object of type id and nominates that as -forwardingTargetForSelector: as well as getting it to respond to -isKindOfClass:, etc.
Disable ARC for the proxy and have it retain itself during init and check its own retainCount when nominating a forwarding target.
If the count is 1 then raise an exception or log a warning or whatever.
Have suspect classes wrap themselves in and return a proxy as the last line of their unit.
For possible bonus points, store [NSThread callStackSymbols] somewhere (probably on disk) during the proxy's unit so you can at least find out where the incorrectly managed object was created.
NSZombies was/is for apps that use their own memory management. If your app uses ARC then this won't help.
Create a new Breakpoint: All Exceptions
That should usually show you where you trigger the bad access.

Is there a way to simulate the process by which iOS releases orphan objects?

I have a popular iOS app, but I get a handful of crash reports that are always on the same line. I can't reproduce the bug for the life of me, but I suspect it has to do with my 3rd-Party library that doesn't use ARC, and so something is getting released when it shouldn't be.
I've tried simulating a memory warning and I've tried taking random globs of memory using malloc, and I can't reproduce the bug. But it happens often enough for many people to email every day and complain about it.
I know that the OS does some "cleanup" that releases objects that need to be auto-released, but is there a way to force this in a simulator?
A message is being sent to a deallocated object.
Either something is trying to talk to a deallocated DBRequest, or DBRequest is trying to talk to a deallocated object.
The most common cause of this is if you do something like:
[DBRequest setNetworkRequestDelegate:self];
DBRequest *myDBRequest = [DBRequest initWithURLRequest:request andInformTarget:self selector:#selector(doSomething)];
You then start some network activity, the user moves to another view, which deallocates self, the network activity finishes, and tries to inform self that it's completed.
Make sure you are calling [myDBRequest cancel]; in 100% of the cases where the object that would be notified is going to be deallocated. The dealloc method is usually a safe place for this.

When will my extern variable released using ARC?

I have a setup where all of my singletons' live in an extern NSDictionary (I need it globally visible, since I make many subclasses from a base singleton object with some common features).
Everything is fine, but on an iPad 1 running iOS 5.0, when user puts the application to background (not terminates, just press the home button), this dictionary gets released (so all of my singleton services, subclasses, etc.). The more interesting, they get recreated when I switch back to the application, but "sometimes" they're not, and my application behaviour gets unpredictable.
I've put __strong before the declaration, but it results in the same. It is quiet harmful when my singletons are destroyed/created all over the time, since they are storing runtime user/application states.
It is important that I'm debuggin with Fastest, Smallest Optimization Level to simulate production environment.
Is there any way to prevent this behaviour? To make/mark it "really retained" somehow?
So long as the application is "alive", they shouldn't be released, ever.
If your application is fully terminated (restarting simulator / closing it from the iPad), everything is released and when you open your app again you won't have anything.
Also, the point of a singleton is that you call a getter and it checks for its existence, and creates it if it doesn't, so there shouldn't be a problem if you don't have it at some point.
If you want persistent data when you re-launch your application, I suggest you look into serialization and/or some kind of persistence

What is NSZombie?

I've seen suggestions saying to set NSZombieEnabled to true while debugging. What is NSZombie? Is it a framework? A setting?
It's a memory debugging aid. Specifically, when you set NSZombieEnabled then whenever an object reaches retain count 0, rather than being deallocated it morphs itself into an NSZombie instance. Whenever such a zombie receives a message, it logs a warning rather than crashing or behaving in an unpredictable way. As such, you can debug subtle over-release/autorelease problems without advanced tools or painstaking needle in haystack searches.
The name is a fairly obvious play on the fact that objects are normally considered "dead" when they reach retain count 0. With this setting, they continue to exist in a strange half-life - neither living, nor quite dead. Much like real zombies, except they eat rather fewer brains.
Adam did a great job explaining what Zombies are, but using the environment variable is not the best way to find and track these.
A much better approach to zombie detection, is just to use Instruments - from XCode start with "Run with Instrument" and choose "Allocations".
Then stop the recording right after it starts, press the "i" button on the Allocations instrument, and turn on "enable reference counts" and "Enable NSZombie Detection". Now hit Record again in the instrument, and your app will start up - if any zombie objects are sent messages recording will stop, and a dialog box will pop up in the recording timeline - you can click on that to find every place an object was retained or released.
Edit: Previous advice was for XCode 3, here's an addition for XCode 4:
In XCode 4.2, there's an even easier mechanism to make use of Zombie detection - the Zombie Instrument. Instead of "Run" to start the app, use "Profile" and an instrument selector will come up. Select "Zombie", and the app will start running - do whatever causes your crash, an a dialog will pop up saying "Zombie Messaged".
From there, click the small arrow in the dialog box. That will take to a list of all the times that zombie object was created, retained, or released. Pull up the side bar and you can go to each entry, looking at the stack trace for the code that was responsible for each adjustment in the retain count.
I agree with what Kendall added, it's very useful, but I'll suggest still doing the environment variable so you don't forget they're enabled. Similar to the (now expired) link at Cocoa Dev, I put this so I don't miss it:
if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled")) {
NSLog(#"ZOMBIES/AFOC ARE ENABLED!!! AAAAARRRRRRGH!!! BRAINS!!!");
}
It catches my attention very nicely.
Would help someone.
Detailed document on Instruments.
https://developer.apple.com/library/watchos/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/index.html#//apple_ref/doc/uid/TP40004652-CH3-SW1

Resources