Using NSMutableArray as a queue not working - objective-c

Strange issue while using NSMutableArray as a queue.
Given a singleton object SO, a ViewController object VC, and a NSMutableArray MA.
MA is being used as a queue for an ID that is received by SO, and passed to VC via putting the ID in MA and sending a notification that VC handles. There is a problem with MA losing what it holds.
Steps relating to issue:
Seque is initiated to load a view.
SO requests an ID from a server.
VC’s viewDidLoad completes
SO block function receives an ID and puts it in MA, and sends a notification to VC.
VC receives notification and retrieves ID from MA
The first time the view loads there is no problem. However if I go out of the view and back into it repeating the steps above the size of MA in step 4 is 1, and the size of MA in step 5 is 0. I.e. MA becomes empty!
I’m perplexed as to how MA mysteriously becomes empty between the time the notification is sent (step 4), and when the notification handler is called (step 5). I’ve checked that nothing outside of the VC notification handler is clearing the array.
A possible clue is that there is a method called (message sent in obj C parlance) from viewDidLoad that if commented out the issue described doesn’t happen. Also if I put the following line after the method in viewDidLoad (instead of commenting it out) then the issue stops occurring:
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow: 0.5]];
I’ve been scratching my head on this one. My latest thought is that there is something about NSMutableArray that I don’t understand. I thought MA was storing strong references to IDs placed in it, but if it weren’t then maybe there’d be some strangeness.
Anyone have ideas?

The problem was that NSNotificationCenter addObserver: was called every time VC's viewDidLoad completed(step 3). This was causing the notification handler to be called as many times as it was added, even if the notification itself was posted only one time.
The following is related: How to stop the Observer in NSNotification to called twice?

Related

An Objective-C message was sent to a deallocated object (zombie) at address: 0x75d52a0

I am relatively new to iOS development so appreciate your help in finding out the root cause for the error that I encountered.
I tried to debug the error using Instruments (i.e. Allocations-Zombie Profiler) but I could not make sense of the call stacks that were presented.
This is how the user interfaces are linked:
TabBarController -> NavigationController -> TopPlacesTableViewController -> RecentPhotosTableViewController -> PhotoViewController
The error occurs when I click on the Back button in the last view (i.e. that of the PhotoViewController). This action is supposed to show the previous RecentPhotosTableViewController but instead an unknown deallocated object was accessed, sometime in between the events of viewWillAppear and ViewDidAppear.
Additionally, I have a GenericTableViewController which is the parent of TopPlacesTableViewController and RecentPhotosTableViewController. The children set a NSMutableArray property in the parent which is the data that gets loaded in the children's views.
I am currently using iOS6 and XCode4.5.
[Update: In the Console, this line was shown - "[UIView _forgetDependentConstraint:]: message sent to deallocated instance xxx"].
I feel you are not using ARC, and you are not retaining of passing your previous object. In the meantime the previous object is released and then you accessing it.
Either you can refactor your code to use ARC or put retain or autorelease.
Go to Product > edit scheme >Diagnostics tap then check on enable Zombie objects
make a break point and go step by step to know which object is deallocated, it perhaps the pointer to your object has been removed then the OS has deallocated your object.

Protecting my code from zombies from completion blocks

I'm familiar with the delegate pattern and nilling my delegates, especially when doing asynchronous calls which are still in progress when my view controllers disappear. I nil the delegate, and the callback successfully returns on a nil object.
I'm now experimenting with using completion blocks to make my code a little easier to read.
I call a network service from my view controller, and pass a block which updates my UITableView. Under normal circumstances it works fine. However, if I leave the view before it completes, the completion handler block is executed - but the UITableView is now a zombie.
Whats the usual pattern for handling this?
UPDATE WITH CODE SAMPLE
This is an iPad app, I have two view controllers on screen at once, like a split view. One is the detail, and the other is a grid of images. I click an image and it tell the detail to load the info. However, if i click the images too fast before they have chance to do the network call - I have the problems. On changing images the code below is called which counts the favourites of a image....
So here is my dilemma, if I use the code below - it works fine but it leaks in instruments if you switch images before the network responds.
If I remove the __block and pass in self, then it crashes with zombies.
I can't win... I'm sure i'm missing something fundamental about using blocks.
__block UITableView *theTable = [self.table retain];
__block IndexedDictionary *tableData = [self.descriptionKeyValues retain];
FavouritesController *favourites = [Container controllerWithClass:FavouritesController.class];
[favourites countFavouritesForPhoto:self.photo
completion:^(int favesCount) {
[tableData insertObject:[NSString stringWithFormat:#"%i", favesCount]
forKey:#"Favourites:" atIndex:1];
[theTable reloadData];
[tableData release];
[theTable release];
}];
Any tips? Thanks
SECOND UPDATE
I changed the way I loaded the favourites. Instead of the favourites being a singleton, I create an instance on each photo change. By replacing this and killing the old one - the block has nowhere to callback (i guess it doesn't even exist) and my code now just looks like the below, and it appear to be working:
[self.favourites countFavouritesForPhoto:self.photo
completion:^(int favesCount) {
[self.descriptionKeyValues insertObject:[NSString stringWithFormat:#"%i", favesCount]
forKey:#"Favourites:" atIndex:1];
[self.table reloadData];
}];
It doesn't leak, and doesn't appear to be crashing either.
I recommend you test that the tableview is not nil at the start of the block. It sounds like the tableview is properly discarded when its parent view goes off-screen, so after that point, no tableview operations are valid.
Retaining the UITableView within the block is a bad idea, because datasource/tableview updates can result in implicit method calls and notifications that will not be relevant if the tableview is not on-screen.
Block will retain any object that it references, except for those annotated with __block. If you want not to execute completion blocks at all, just make some property like isCancelled and check whether it is YES before calling completion block.
So you have a background operation which has to call back another object after it finishes and the object can be destroyed in the meantime. The crashes you describe happen when you have non retained references. The problem as you see is that the referred object goes away and the pointer is invalid. Usually, what you do is unregister the delegate inside the dealloc method so that the background task continues, and whenever it is ready to communicate the results back it says "Shoot, my callback object is nil", and at least it doesn't crash.
Still, handling manually weak references is tedious and error prone. You can forget to nil a delegate inside a dealloc method and it may go without notice for months before you encounter a situation where the code crashes.
If you are targeting iOS 5.0 I would read up upon ARC and the weak references it provides. If you don't want to use ARC, or need to target pre 5.x devices, I would recommend using zeroing weak reference libraries like MAZeroingWeakRef which work also for 3.x devices.
With either ARC's weak references or MAZeroingWeakRef, you would implement the background task with one of these fancy weak reference objects pointing back to your table. Now if the pointed object goes away, the weak pointer will nil itself and your background task won't crash.

use applicationDidBecomeActive to call viewDidLoad

I want to make sure that all my initializations for my views and stuff are handled every time my application starts, even when it is called back after being sent to the background, such as with multitasking.
What's the best way to do this? should i use applicationDidBecomeActive to call viewDidLoad on my viewcontroller directly? I'm guessing this is not wise. I just want to make sure that stuff gets done on load every time the user calls up the app, no matter what state it is in at the time.
I have several apps published that do just that - call viewDidLoad on one or several UIViewControllers from applicationDidBecomeActive.
It works just fine. Apple doesn't object to it either.
However, be aware that if you have allocations in your viewDidLoad you need to either check for already allocated instances or release the instances you allocate in viewDidLoad when your app suspends. The same goes for DB connections that need to be closed, notification listeners, and so on.
As long as you watch for these elements and handle them correctly, the approach is valid and very usable.

ASIHTTPRequest popViewControllerAnimated: => delegate EXEC_BAD_ACCESS

I set the view controller to be the delegate of a local variable ASIHTTPFormDataRequest request.
But, tapping "Back" before the request has finished, pops and deallocates the view controller. So, when the request completes and sends the message -requestDidFinish: to the now nonexistent delegate, the app crashes with an EXEC_BAD_ACECESS exception.
How do I fix this crash?
One way I could think of solving this is to set the delegate to nil immediately after the navigation controller pops it. But, if that's the solution, how do I do that? (ARC's weak references would be sweet right now.)
Another way I can think of is to make request an instance variable of the view controller and call [request clearDelegatesAndCancel]; [request release]; in the view controller's dealloc method. This approach is outlined in ASIHTTPRequest Example Code, but I've been advised it's best to make requests local variables instead of instance variables. And, this particular view controller is a settings table view controller and has 13 switches. For automatic saving to the server, each switch creates & sends a new request each time it's toggled. If I made ivars, I'd have to make 13. That's a lot of code!
Thoughts? Ideas?
I think the first question is: What do you want to happen if the user presses back after pressing a switch? ie. Should the http request be cancelled, or is it important that the request does get to the server? I'll assume for now that you do want to cancel them, as that seems to be implied in your question.
I've been advised it's best to make
requests local variables instead of
instance variables
I'm not sure if that was good advice - you almost always want requests to not be local variables so you can cope with cases like this.
For your case, you could consider using an NSOperationQueue.
Rough steps to do this would be:
Create an NSOperationQueue in your view controller init.
When you want to make a http request, add it to the ASIHTTPRequest queue instead of call startAsynchronous
In dealloc, iterate the objects in the queue, calling [request clearDelegatesAndCancel]; for each one, and then release the queue.
That should solve the crash without needing 13 ivars!
I solved this by retaining the request delegate like NSURLConnection retains its delegate.

"message sent to deallocated instance 0xec75b0", but 0xec75b0 shouldn't even be visible

I'm working on an iPhone app that performs some communication with the server to be initialized the first time it's run. I have a class ServerCommunication with methods for each of the basic tasks to be performed (authenticate user, get activation key, download updates, etc.), and a new instance of ServerCommunication is created for each task; I'm not reusing them, although it would be more efficient.
When the user completes the first initialization screen, ServerCommunication gets created four times. I keep track of it with NSLog(#"Initializing ServerCommunication instance %p", self); in its -init method. The second initialization screen also calls ServerCommunication a few times when the user taps the "Next" button, but on its last instantiation, the app hangs with the message -[ServerCommunication insertDataIntoLocalDB:]: message sent to deallocated instance 0xec75b0 in the console. The thing is, 0xec75b0 is the address of the first instance of ServerCommunication I created way back at the first screen.
Why would it be sending messages to that instance? I'm not retaining them anywhere; they're mostly autoreleased. If it helps, all of the methods in that class perform asynchronous downloading of XML data with NSURLConnection and then parse it with NSXMLParser. The parser's delegate method -(void)parserDidEndDocument:(NSXMLParser *)parser then sends off NSNotifications that are received by methods in my view controllers so they know whether to proceed to the next screen or stay there and display an error message.
Help is much appreciated!
The first thing I would do is turn on NSZombies, which should let you break at the point where your zombie is being messaged.
A common cause of problems like this is when you have objects with weak references to each other that are not allocated and deallocated at the same time. So (hypothetically), some other object stores a pointer to your ServerCommunication object as a delegate or owner. When ServerCommunication is deallocated, it doesn't unregister, and then some time down the road the object holding the weak reference tries to message you.
If I had to completely guess (and I do!) I bet you add your ServerCommunication objects as an NSNotification observer, but never remove them. Try making sure that you do this:
[[NSNotificationCenter defaultCenter] removeObserver:self];
sometime before deallocation. (It's also possible that there's a more circuitous path involving NSNotification here -- such as a pointer to the ServerCommunication object being passed as data to the view controller, which is then trying to message it.)