I am using ASIFormDataRequest class to send and receive data from server to iphone client. I have a problem. For example, I have a View Controller using ASIFormDataRequest, if the current view is View Controller, the method requestFinished of ASIFormDataRequest delegate called successfully. But in some case, I pop back to the previous view of View Controller, that means the View Controller release, the ASIFormDataRequest object of View Controller class is also released. But the requestFinished still called. I debug and I see that View Controller has become a Zombie object, I don't know why the requestFinished method been called even object release. I also set in the dealloc method of ViewController
- (void) dealloc {
asiFormDataRequest.delegate = nil;
[super dealloc];
}
I think, better use your request not in this ViewController class, but create some independent class, and use from there you ASIRequests, using this class as delegate
btw, it helps me with similar situation
Related
I have a problem. My view controller (ViewController) implement a delegate method of a object (DataPuller, data get from the internet). DataPuller will retrieve data on the internet without blocking user interaction with the view. But when I navigate between screen, in some cases, that ViewController release before DataPuller return the list of objects. The DataPuller return, it checks:
if (delegate && [delegate respondsToSelector:#selector(getCommentDidDownloadFinish:)]) {
[self.delegate performSelector:#selector(getCommentDidDownloadFinish:) withObject:self];
}
And the application crash here because ViewController release, it becomes a zombie object. Does anyone have this problem before and how to solve it? I think another way is using NSNotification, but I wonder any other better solutions. Any ideas, solutions are welcomes. Thanks.
Your view controller must remove itself as the DataPuller delegate at some point. Typicially, this is handled in the dealloc method:
- (void)dealloc {
dataPuller.delegate = nil;
[dataPuller release];
[super dealloc];
}
You may also decide to do this in -viewDidUnload or -viewDidDisappear:.
Delegation (usually) implies some sort of ownership - i.e., if you make an object a delegate of another object, usually the delegate object holds a strong reference (i.e., retains) the delegating object.
As an example, a UITableViewController is the delegate of its UITableView. This is okay, because the controller retains the tableview through the "view" property.
If your design does not allow ownership, use notifications, like you already suggested. As a bonus, notifications can signal multiple listeners if you would ever need that.
Don't forget to remove your observer in the dealloc of the view controller!
Is it safe to assume that an attribute, namely fetchedResultsController, of chatViewController, an instance of a subclass of UITableViewController, is always nil when viewDidLoad is called, assuming that it's set to nil in viewDidUnload? Phew!
If that's the case, then I see no immediate need to redefine the accessor function like in the Xcode example application CoreDataBooks. I'd rather just put all that code in viewDidLoad instead of in a separate function because that's the only place I'll use it.
viewDidLoad is called after your view is loaded. Whether or not fetchedResultsController is nil or not depends on how the viewController is initialized. For example, when creating the detailViewController, you could set its fetchedViewController before viewDidLoad is called:
RecipeDetailViewController *detailViewController = [[RecipeDetailViewController alloc] initWithStyle:UITableViewStyleGrouped];
detailViewController.fetchedResultsController = fetchedResultsController;
[self.navigationController pushViewController:detailViewController animated:animated];
[detailViewController release];
That said, then nil'ing fetchedResultsController in viewDidUnload would ensure that it's nil.
ViewDidLoad Called in These Secnarion:-
1.when we push the view controller after creating it’s object by segue or by stoary board id.
2.it called more than one in the case of creating instance more time in application and push it again and again.for example:-if you implement like coaursal(that having required to additional controller during scrolling) like that it’s need so it can called multiple times viewDidLoad.
3.it called when all memory instance (uiviewcontroller and it’s subclass instantiated) that means when our view is ready to load in memory with the address.
4.Remember only child class controller object is created..parent class object never been instantiated during normal Secnarion.
You have to assume that viewDidLoad can be called multiple times. If there is a memory warning sent, your view controller will unload the view from memory, and the next time it is needed viewDidLoad will be called.
viewDidLoad is called only when view is instantiated for first time . If you're not recreating the view controller each time in your application, you'll only get it called once (and called again if you get a memory warning, and the view is nil'd out).
I have a controller that makes HTTP GET requests using a custom class, which acts as the delegate for the NSURLConnection. Once the NSURLConnection fails or finishes, the custom class invokes methods on the controller and passes along an NSData object of received data.
I'm running into a problem where the controller in action is being dynamically created and pushed onto a navigation controller's stack. This controller makes the HTTP GET request in its viewDidLoad method. If the user quickly presses "back" on the navigation bar, this controller gets dealloc'ed. If this happens before the HTTP GET request finishes, the resulting NSURLConnection callback becomes a method call to a dealloc'ed object, which results in an EXC_BAD_ACCESS.
What's the best approach to cleaning up any pending NSURLConnections that have been kicked off by a controller which may actually be deallocated already?
I threw in some NSLog statements, and it seems that my custom class used as a NSURLConnection delegate doesn't actually receive a dealloc message. I made sure to set the controller's instance of this class to nil in viewDidUnload, and also call release on it, but it still seems to live longer than the controller.
If I understand it correctly you just need to do [whateverConnection cancel] in your viewDidUnload or dealloc method. This cancels the connection. It is almost the same if you have a custom downloader object for example for some large image that uses NSURLConnection. Make a cancel method for your class (that cancels the connection and releases it) and invoke it in your controller's dealloc method. You should also use a bool flag something like wasCanceled and do not invoke any method from your custom object's delegate if wasCanceled was set from your cancel method. (You only have a weak pointer to your delegate so it is probably released already when some other object invokes your cancel method). I assume that the delegate for your custom object is the view controller. I had several downloaders like this and it worked ok, without leaks even if I quickly canceled a download.
#interface CompaniesDownloader : NSObject /*<NSXMLParserDelegate>*/
{
id<CompaniesDownloaderDelegate> delegate; //a view controller is the delegate
NSMutableArray *companies;
BOOL isWorking;
BOOL wasCanceled;
#private
//url connection object
NSURLConnection *companiesConnection;
//this is where i put the binary data that gets transformed into xml
NSMutableData *webData;
//temporary string used when parsing xml
NSMutableString *tmpString;
//temporary company used when parsing xml
Company *tmpCompany;
}
In the implementation:
-(void) cancel
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible: FALSE];
wasCanceled = TRUE;
[companiesConnection cancel];
[webData release];
webData = nil;
self.companiesConnection = nil; //OR [companiesConnection release]; companiesConnection=nil;
isWorking = FALSE;
}
You need to retain your view controller when you make a request and release when the get request finished
YourViewController.m
- (void)callGetRequest {
[self retain];
}
- (void)didFinishAllGetTask {
[self release];
}
If the user quickly presses "back" on the navigation bar, this controller gets dealloc'ed. If this happens before the HTTP GET request finishes, the resulting NSURLConnection callback becomes a method call to a dealloc'ed object, which results in an EXC_BAD_ACCESS.
When the user hits the back button, de-register the view controller class from being the delegate for that object. (Keep a reference to the object in the view controller class, so you can do something like someObject.delegate = nil;). You can do that in the view controller's dealloc method.
I am using an UITableView to show some string messages, and I use NSOperationQueue to hold an customized NSOperation which fetchs message in background thread. After one message fetched successfully, customized NSOperation will notify the UITableView controller to show it.
If I click back button on the navigation bar to switch from the UITableView to other view after all message loaded, every thing is OK. But, if i click the back button while some message still loading, an EXC_BAD_ACCESS is throw. I have checked that the exception happened while customized NSOperation notify UITableView controller with performSelectorOnMainThread method. Sound like the target UITableView controller is not invalid after view switched, but I think Navigation Controller will hold the view controller instance. May I know how to resolve this issue? Thanks.
Customized operation is initialized in the UITableView controller with following code:
StatusMessageLoadingOperation *operation = [[StatusMessageLoadingOperation alloc]
initWithData:person
messageArray:cachedStatusMessages
target:self
action:#selector(didFinishStatusMessages:)];
[operationQueue addOperation:operation];
[operation release];
The customized NSOperation class will update UITableView with following code:
- (void)main{
for (int i = 0; i < [[person statusMessages] count]; i++) {
[target performSelectorOnMainThread:action withObject:messageArray waitUntilDone:NO];
}
}
Have you tried calling [operationQueue cancelAllOperations] in your viewWillDisappear method?
Because popping a view controller calls that controller's -dealloc method, you may be releasing your queue too early, and some other part of your application is trying to access the queue or an operation inside it that no longer exists.
My recommendation is to put your NSOperationQueue *myQueue instance into your application delegate.
Use the app delegate's -applicationDidFinishLaunching: and -dealloc methods to initialize and release your queue and its contents.
By separating your queue from the view controller, your queue won't get released when you pop off a view controller from your navigation stack. It and any remaining operations should still be available to the rest of the application.
To make it easier to access your queue, set up the following macro definition:
#define UIAppDelegate ((MyAppDelegate *)[UIApplication sharedApplication].delegate)
You can then use the macro to access your queue as follows, e.g.:
NSLog(#"%#", [[UIAppDelegate myQueue] operations]);
Or, for example:
[[UIAppDelegate myQueue] addOperation:myOperation];
I have an view controller class. I want to do some things in my view when the accelerometer detects movement and calls the accelerometer:didAccelerate: method in the delegate object.
That delegate object is the problem here in my brain. Currently, my brain is freezed and I don't get it what would be better. Let me know what you think!
Solution 1)
In my view controller class I conform to the UIAccelerometerDelegate protocol, and implement that accelerometer:didAccelerate: method.
In the -applicationDidFinishLaunching: method of my AppDelegate class I set that view controller object up as the delegate for receiving method calls upon accelerations. I think that's not really good.
Solution 2)
I create a blank new object called AccelerationDelegate, conform to that UIAccelerometerDelegate protocol, implement that accelerometer:didAccelerate: method and in the -applicationDidFinishLaunching: method of my AppDelegate class I set that view controller object up as the delegate for receiving method calls upon accelerations.
But for solution 2 my brain got stuck a little bit! How would I access the view objects from my view controller inside that object?
The problem here is, that I have more than one view controller around. I use a tab bar controller to switch between them.
Any suggestions how I could get that right?
I agree that the second method is better. Are you looking to access just the currently selected tab view, or just a specific view in your app.
In any case, what I would do is to set up properties for your UITabViewController in your UIApplicationDelegate so that you can access it from the delegate (you can get the app delegate by calling [[UIApplication sharedApplication] delegate]). For example:
YourApplicationDelegate *appDelegate = (YourApplicationDelegate *)[[UIApplication sharedApplication] delegate];
FirstUIViewController *firstViewController = appDelegate.firstViewController;
[firstViewController doStuff];
where firstViewController is a property on your delegate.
If your acceleration is specific to one view controller, then it makes sense to have the view controller receive the information necessary to alter its own subviews. However, it might be better to set your view controller to be the delegate when the view appears, and set the delegate to null when it disappears. (Specifically, - (void) viewWillAppear: and - (void) viewWillDisappear:)