EXC_BAD_ACCESS when adding and removing a view from different views - iphone

I have created a game with a in game shop view and view controller.
The shop can be accessed in then menu (ViewController.m) and from the Game Over screen (GameViewController.m).
My problem is that if I have displayed the shop once in the menu, and then play a game and access the shop in the game over screen and try to buy something, the app crashes trowing a EXC_BAD_ACCESS error without much info. (Breaking at
[[SKPaymentQueue defaultQueue] addPayment:lPayment];
line in the ButtonPressed action in my ShopViewController when trying to buy a IAP.
My view are set up like this:
Menuview -> Ladderview -> Gameview -> ShopView
and
Menuview -> Shopview
Hope you can help me pinpoint the error,
EDIT -----------
It seems that I can reproduce the error from the menu -> Shopview without using the game view. I can do this by pressing a "buy button", pressing cancel, navigate back to the menu, go back to the shop, and repeat. On the 3-4th attempt it crashes at the same line. Here is the entire button pressed method:
- (void)buyButtonPressed:(UIButton *)pButton {
NSInteger lTag = [pButton tag];
//////NSLog(#"Button tag: %i"), lTag;
Reachability *lReachability = [Reachability reachabilityForInternetConnection];
NetworkStatus lCurrentNetworkStatus = [lReachability currentReachabilityStatus];
if (lCurrentNetworkStatus != NotReachable) {
if ([SKPaymentQueue canMakePayments]) {
SKPayment *lPayment = [SKPayment paymentWithProduct:[mPriceArray objectAtIndex:lTag]];
[[SKPaymentQueue defaultQueue] addPayment:lPayment];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
} else {
[self showAlertViewWithText:#"Purchases are disabled. Please check your settings for General -> Restrictions -> In-App Purchases and try again." andTitle:#"Warning"];
}
} else {
[self showAlertViewWithText:#"No network connection!" andTitle:#"Warning"];
}
}
So it might seem as the lPayment is being deallocated. I even tried to set
mProductIds = nil;
mPriceArray = nil;
when I remove the shop view, trying to force it to allocate it again when I reload the shop, but without any luck.
Thanks

Your issue is a dangling pointer. EXC_BAD_ACCESS is the CPU moaning that you are addressing non-existent memory or memory which is outside of your access rights area. The cause is a lack of retainment of an object which causes early deallocation and then is overwritten. At which time (which may be delayed), the pointer will point to garbage whose dereference (class examination) causes an EXC_BAD_ACCESS to be thrown. This error canNot be caught using #try. There is an assumption here that the stack itself is corrupt causing continuation to be impossible (although such is most likely not the case), which will throw the debugger for a spin, whose current state output is already lacking in many areas. It is like uncontrollable anarchy when the CPU resets important registers and performs a long jump.
consider Automatic Reference Counting. In you are already there, consider that delegate-like properties are not retained by the host object. Any property which could logically contain self will not retain any value stored in it. ARC will not help you there.
in your case: defaultQueue is probably good. lPayment has probably been deallocated.

Try to trace the problem at first enabling NSZombie . In case of EXC_BAD_Access Problem some time it(NSZombie ) becomes more useful to trace deallocated object than simple guessing where the problem is.

It is hard to tell from the information provided, but it could be the following: Your statement
SKPayment *lPayment = [SKPayment paymentWithProduct:[mPriceArray objectAtIndex:lTag]];
instantiates an SKPayment object, and hands it over to the current autorelease pool. If this pool does not exist (this might be the case if the code runs in a separate thread for which no autorelease pool has been set up explicitly), the object is released immediately again, and your statement
[[SKPaymentQueue defaultQueue] addPayment:lPayment];
accesses invalid memory.

Related

App crash on [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];

That's all I can get from the crash when I do in the applicationDidBecomeActive
MyStoreObserver * observer = [[MyStoreObserver alloc] initWithContext:self.managedObjectContext andDelegate:self];
[[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
But the weird thing is I have been able to purchase twice in the past and even if I delete the app re-reun everything I cannot escape this pit. It doesn't seems logic. I can alloc init the observer alone without problem and I can call the default paymentQueue without problem but if I try to add the transaction observer to the queue, life stop. It's on ARC and the delegate I added to the observer is not the problem. I know for sure.
I've added an nslog to every method in the observer and none get called before the crash
SOLUTION FOUND
Okay, well it looks like the observer is not retained by the queue and thus the observer needs to be an instance variable.
Thanks
Okay, well it looks like the observer is not retained by the queue and thus the observer needs to be an instance variable (or retained in some way).
To add an official source to this answer:
Inside the header file SKPaymentQueue.h of the Framework Storekit is the following clear comment:
// Observers are not retained.
// The transactions array will only be synchronized with the server while the queue has observers.
// This may require that the user authenticate.
- (void)addTransactionObserver:(id <SKPaymentTransactionObserver>)observer __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);

Segmented Control Problem

My program will crash if I press the segmented control buttons one after another. For example, if i press the first one, then second, and third, it will crash. Any ideas for what could be causing this. I also release it and everything, really stumped here. Any ideas.
Thanks
- (IBAction) segmentedControlIndexChanged {
switch (self.segmentedControl.selectedSegmentIndex) {
case 0:
[self mirror];
break;
case 1:
[self exact];
break;
case 2:
[self round];
break;
}
}
I'm getting a EXC_BAD_ACESS
if that means anything, cause i'm not sure what that means
error on line 2179 of "/SourceCache/gdb/gdb-1510/src/gdb/macosx/macosx-nat-inferior.c" in function "macosx_kill_inferior_safe": (os/kern) failure (0x5x)
EXEC_BAD_ACCESS means you are attempting to call a method on a deallocated instance. The tricky thing about fixing these errors is that it can happen well after the instance was released, so what you're doing at the time isn't necessarily what is causing the error.
Fortunately, there is a tool to help you. NSZombieEnabled
Go down to your executables folder in XCode, and right click on the app and click Get Info.
Go to the Arguments Tab, and click the plus button below variables to be set in the environment.
Call the new variable NSZombieEnabled and set it's value to YES
When you enable this, any instance that gets deallocated gets replaced by a Zombie object, and your console should display an object and "message sent to deallocated instance" which should help you track down your problem.

Testing if an object has been deallocated

I have a situation where occasionally my -tableView: numberOfRowsInSection: method asks for the count of a deallocated NSArray. I'd like to be able to test for this array being deallocated in a way that's safe and doesn't make a call to a zombie.
My code currently looks like:
-(NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
if (! self.offers){
return 0;
}
return [self.offers count];
}
I just debugger-stepped through this and observed it passing the ! self.offers test and then crashing brutally on [self.offers count]. I have NSZombies turned on, and at that line I get the NSLog message:
-[__NSArrayM count]: message sent to deallocated instance 0x283dc0
So whatever self.offers is, at this point, is not nil, but is also not pointed at anything valid. How do I test for that?
EDIT: Thanks for the tough-love, friends. I've figured out why I've been having trouble with my memory management--it's actually been an issue of delegate relationships lingering longer than they were useful. See here for the full answer to my problem:
Managing calls to objects that get deallocated when the view is backed out of
It's not possible to "test" for this-- once an object is deallocated, it's gone, and the pointer to it is effectively garbage. Doing anything with it is liable to crash.
Sorry to be the bearer of bad news, but the only way to get this to work right is to make sure the memory management around (in this case) offers is correct at all uses-- retain and release correctly at all the points where that instance variable is manipulated, and it will never be non-nil garbage.
You should try running the static analyzer (Build->Build and Analyze) to start with-- this tool can help flag memory management pattern issues without a lot of extra digging on your part.
You should avoid the problem by fixing the code so it does not deallocate the array when you obviously still need it.
You can add
-(void)dealloc { ... }
And leave it empty if it's the right to do, and add breakpoint in it.
This answer correct to ARC and NON-ARC
This might not be relevant to your question (testing if it is deallocated) - but it seems like your offers array has the same or longer lifetime than your UITableViewController, are you certain you are retaining this array or that you aren't accidentally releasing it somewhere else.
I concur with what quixoto has said above.
Another thing you have to ask yourself is, is my application designed correctly? A tableview relies on a datasource to populate it (which in your case is the offers array), so if you're deallocating it somewhere in the code then you need to step back and rethink your design.
If you're testing for whether it's deallocated because you're trying to 'fix' it from a previous problem, then it might be worth looking into your code to see where it is being retained/released.
Yes, once an object is deallocated, it's gone, and the pointer to it is effectively garbage. Doing anything with it is liable to crash.
However it is POSSIBLE to test. There is a workaround for it. (Although workarounds are not a substitute for good design practices!):
You have a global BOOL variable, which you set on the alloc. Here's an example of use for checking whether a AVAudioPlayer isPlaying. If it was playing, and the view was closed, the app would crash. Simply stopping the music was not an option due to background threads, etc. But calling [newPlayer isPlaying] crashes when newPlayer is already released! So a BOOL flag workaround made it work. (Any good design practice comments on this would be valuable.)
// In implementation
static AVAudioPlayer *newPlayer;
static BOOL hasMusicFinishedPlaying;
// When Playing the music, set flag
newPlayer =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath: soundPath] error:nil];
hasMusicFinishedPlaying = NO;
// Callback method to release new player in playKeySound
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)newPlayer successfully:(BOOL)flag {
[newPlayer release];
hasMusicFinishedPlaying = YES;
}
// On going back to another page. We don't want to keep ViewController in stack.
if (!hasMusicFinishedPlaying){
return;
}
[self.navigationController popViewControllerAnimated:YES];

NSZombieEnabled hides EXC_BAD_ACCESS error entirely

So I have a subclass of a UIView that starts causing EXC_BAD_ACCESS errors when I go through a specific set of conditions (run on iPad instead of iPhone or simulator, first login only). It throws the exception when the UIView subclass gets autoreleased from the pool (i.e. the pool is releasing, not when I'm calling [view autorelease], during the last line, where I have [super dealloc]. I heard about using NSZombieEnabled, so I tossed that on to see if I could get any more information about it, but now it hides the error completely!
Does anyone know a bit more about this type of situation? I thought NSZombie would start spewing stuff into my console like before, but I'm hoping that the nonexistance of errors would tell me some sort of information as well.
- (void)dealloc
{
[loadingLabel release];
[indicatorView release];
[super dealloc];
}
Edit: Ok, so I sorta solved the underlying problem:
One of my properties was:
#property (nonatomic,retain) NSString * title;
However, the code for that property is as follows (loadingLabel is a UILabel):
- (void)setTitle:(NSString *)title
{
loadingLabel.text = title;
[loadingLabel sizeToFit];
[self setNeedsLayout];
}
- (NSString *)title
{
return loadingLabel.text;
}
I don't actually retain anything, but rather only do a UILabel.text, which is a copy property. So I changed my own title property to reflect this, and the error has disappeared.
However, I still don't really know how or why this error popped up in the first place, and why it only appears on the iPad platform (not the iphone, or even the ipad simulator).
So I have a subclass of a UIView that starts causing EXC_BAD_ACCESS errors when I go through a specific set of conditions (run on iPad instead of iPhone or simulator, first login only). It throws the exception when the UIView subclass gets autoreleased from the pool (i.e. the pool is releasing, not when I'm calling [view autorelease], during the last line, where I have [super dealloc]. I heard about using NSZombieEnabled, so I tossed that on to see if I could get any more information about it, but now it hides the error completely!
You get EXC_BAD_ACCESS because you tried to work with a dead object.
NSZombieEnabled makes objects not die. Since the object never died, your app didn't crash. Instead, you'll get a message in the Console log (the Debugger Console when running under Xcode), telling you what you did wrong and suggesting a breakpoint to set.
To be more specific, the NSZombie message will tell you what class of object you sent a message to, and what message you sent it. If you set the breakpoint and run your app with the debugger active, the debugger will interrupt (“break”) your app at that point, so that you can look around and see what sent the zombie object the message.
I don't actually retain anything, but rather only [assign to] a UILabel.text, which is a copy property.
Thus, the string presumably died off for not being owned by anything. With NSZombieEnabled and the above technique, you can confirm the theory that it was the string that was dying off prematurely.
A better and easier way, though, is to use Instruments's Zombies template. Instead of a message appearing in Xcode's Debugger Console, a flag in will appear Instruments's timeline with the information. That flag will have a go-to-iTunes-Store (➲) button you can click for more information about the problem.
Could you clarify - do you mean that you throw an EXC_BAD_ACCESS exception or you (on purpose) cause the system to throw an EXC_BAD_ACCESS exception? How do you do this? Please show some code. NSZombie will spew to the console if you try to call an instance method on it. Are you doing that?
NSZombie should produce output to the console in the same location a crash would have occurred.
Have you tried also running Instruments with the ObjectAlloc tool, and enabling both retain count and zombie detection in there (you need it both in the executable and configure the ObjectAlloc instrument, click on the "i" next to ObjectAlloc). When a message is sent to a zombie that instrument will flag you with a special popup saying a zombie was detected.
Remember the iPad SDK is still beta, maybe you should report this but to apple if you can confirm this to be a bug. Unfortunately their bugtracker is horrible from my experience.
The behavior of NSZombie is to never decrease an objects retain cound if release is called. However it keeps track of the would-be retain count and logs acess to the object after it would have been deleted on the console. It puts the Object adress alongside the error message, together with Instruments you can easily locate the Object at that adress and determine where it has been alocated and who retained/released it.

Is calling [self release] allowed to control object lifetime?

I want to create an object in Objective C but I don't hold a reference to it.
Is it allowed to let the object control its own lifetime by calling [self release]?
In case you're wondering why I need this: I want to create an object that subscribes to some notifications, but after a while the object is no longer needed and should go away.
So, is the following allowed?
- (void) destroyMyself {
 [[NSNotificationCenter defaultCenter] removeObserver:self];
[self release];
}
If you see this in code, its probably wrong. However there are legitimate response for it in certain circumstances that are arguably defensible. (So make sure you are doing it for the right reasons.)
A good example of when this makes sense, is when you create an object that goes off to download a url. The object sits in memory while downloading the url, then sends a message to its delegate saying the data is ready (or url couldn't be downloaded). Once its message has been sent it destroys itself as its no longer needed. In this situation the code/function that created the 'url downloader' may no longer even be in memory, i.e. if it was called in response to a user selection a menu item or an action in a view controller that is no longer on the screen.
This is useful when the code that creates the "download" object doesn't care if the download completes or not.
The rules are simple. You should only release an object if you own it. i.e. the object was obtained with a method starting "new" or "alloc" or a method containing copy.
Cocoa Memory Management Rules
An object must not therefore do [self release] or [self autorelease] unless it has previously done [self retain].
To quote the great philosopher Alicia Silverstone, "I had an overwhelming sense of ickiness" when I read that. But I couldn't really tell you why.
I think I would use autorelease rather than a simple release since you're still executing code in self when you call it, but other than that I can't think of any technical reasons why it wouldn't work.
It's legal, but be careful. You want to be sure nothing else is going to send you a message after you release yourself.
I've done this kind of thing for a faulting scheme back before we had CoreData.
Well part of the protocol is that if you send release to self, then you should have sent retain once as well, which I suppose you do. Then there is nothing fishy. I mean the allocing code must be able to control the lifetime of your instance; it itself can only prolong its life, never make it shorter (since making it shorter, then you'd suddenly leave the allocing owner of the instance with an invalid pointer).
And I will use [self autorelease] instead of [self release]. Because usually it's called in
- (void)aMethod
{
[self.delegate aDelegateMethod:self];
[self release];
//If you add code related to self here, after [self release], you are making a huge mistake.
}
If I use [self autorelease], I can still do something after autorelease.