i didn't really know how to title this question, but here's a thing that really kills me:
In my app i have a UITableView, UISegmentedControl and UINavigationBar. Once UISegmentedControl gets one of its segments selected i want to show a UIActivityIndicatorView on the UINavigationBar and then parse an xml file and present the results in a table.
Everything works almost as i want it to, except one thing, the activity indicator view gets added to the uinavigationbar after the parser finishes, even though the method showLoading that adds UIIndicatorView to UINavigationBar gets before parser is initialised.
Can anyone explain it? is there something i might be missing? maybe the ui needs to get redrawn?
thanks
peter
It looks that you parse your xml in main thread and so it becomes blocked for UI changes. Try to move xml parsing to separate thread (e.g. by calling your parsing method via -performSelectorInBackground:)
Edit: Actually you're (almost certainly) using autorelease implicitly in your application - as many standard functions return autoreleased objects. When you're running your functions on separate thread you need to create NSAutoreleasePool object there to handle autoreleased objects and avoid memory leaks (see Autorelease Pools in docs). So your parseXML function must look like:
- (void)parseXML{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
... //xml parsing routines etc
[pool release];
}
Related
I have created a custom UIView called CustomMessage that I am using throughout my program. The appearance of the CustomMessage is animated so I have written a method in the CustomMessage class called showInView: to show the view. For example, say that I wish to show the CustomMessage view in a particular view controller - I would use the following code:
CustomMessage *myCustomMessage = [[CustomMessage alloc] initWithMessage:#"Hello"];
[myCustomMessage showInView:self.view];
As you can see, this is quite similar to how a UIActionSheet is created and presented.
However, I am having problems with the memory management. If I put the following line of code directly following the two lines above:
[myCustomMessage release];
then (as expected) the program will crash with the message sent to deallocated instance error.
I am unsure what I need to do in my CustomMessage class so that I can release the object directly after calling the showInView: method so that I don't get a memory leak. Obviously this can be done, since that's how a UIActionSheet works (but I just can't get my head around how I can implement something similar - I can't figure out how the CustomMessage object can be retained by some other object, presumably the self.view (in the example above) which is displaying the CustomMessage, to avoid it releasing the object entirely while it is still in use).
Everything else works perfectly except for this little aspect, so any help would be greatly appreciated :)
I'd need to see your code for showInView: in CustomMessage.m. I would expect it to look something like the following.
- (void)showInView:(UIView *)view
{
// pre-animation configuration
[view addSubview:self];
// do the animation
}
This means that the CustomMessage instance is retained by view. Also, if your implementation of showInView uses concurrency at all it is possible it is returning immediately allowing MyCustomMessage to be released and then background operations are trying to access the release object. I'm just guessing w/o seeing your code.
What is the crash log? What message is sent to what object in what context? This info will localize the problem.
If none of the above helps you solve the problem, post your code for showInView as well as details from the crash log and I'll take another look.
I have a simple query that I'd like cleared up by someone... Is it bad-practice to retain self?
I have a server request object that I'd like to make. I'd like to be able to use it in the following fashion:
ARequest *request = [ARequest request: someParam];
request.delegate = self;
[request begin];
In order for the object not to self destruct as soon as the autorelease pool is drained, I imagine I need to call a retain in it's init method and then a release once the server response has been received, processed and delivered to it's delegate.
However, something is raising a warning bell in my head with this approach. Better ways to do it?
There is nothing wrong with retaining self, as long as you release it at some well-defined point in accordance with normal memory management protocol. If an object requires itself to exist until some condition is met, it should take responsibility for that, in the same way as it does for any other object it requires to continue existing.
Introducing otherwise extraneous manager objects or foisting the responsibility off on the object’s owner for superstitious reasons would be the real anti-pattern here.
(The equivalent approach in garbage-collected code would be for the object to exclude itself from garbage collection while results are pending, or root it through a collection of some sort if you dislike that idea.)
It's not unheard-of, but it is somewhat uncommon. The main way I've seen it used (and used it myself) is when you're dealing with some sort of semi-synchronous object (by semi-synchronous I mean that it does not block the main thread, but it also does not execute on a background thread; an NSURLConnection would fit this bill). For example, I wrote a subclass of NSWindowController that was specifically for displaying a window as a sheet and for invoking some certain delegate callbacks. Basically, you'd alloc/init a new sheet controller and invoke beginSheetForWindow:. This would run the sheet semi-synchronously, and then invoke an appropriate callback when the sheet was dismissed.
Since the invoking object doesn't necessarily "own" the sheet (think of it as a Mac version of a modal view controller on iOS), the sheet controller does [self retain] immediately before showing the sheet, and [self release] immediately after cleaning up and invoking callbacks. The purpose behind this was to ensure that the controller object would stick around until the sheet was done. (The sheet, IIRC, was retained by the runloop, but I also needed the controller to stick around)
Like I said, it's very rare to come across a situation where you would want to [self retain], but it's not impossible. However, as a general rule of thumb, if you think that you need to [self retain], you may want to think again.
Easiest way to do this would be to create an iVar for your request, retain the request when you start it and release it when the last delegate method is called.
Is ARequest a class you created? Does it create a new thread to asynchronously submit the request?
I once did the same thing as you. I wrote a Category-Method on NSString to send it it to a server, that will print it. In the Category-Method I had to call [self retain], so that the callback methods could be a NSString-Categroy-Method to.
I felt so bad about it, that I rewrote everything to use a Singleton, that is accessed by the Category-Method. So the Singleton will retain the string as long as necessary.
Here's the issue – I followed along with the Apple lazy-load image sample code to handle my graphical tables. It works great. However, my lazy-load image tables are being stacked within a navigation controller so that you can move in and out of menus. While all this works, I'm getting a persistent crash when I move into a table then move immediately back out of it using the "back" button. This appears to be a result of the network connections loading content not being closed properly, or calling back to their released delegates. Now, I've tried working through this and carefully setting all delegates to nil and calling close on all open network connections before releasing a menu. However, I'm still getting the error. Also – short posting my entire application code into this post, I can't really post a specific code snippet that illustrates this situation.
I wonder if anyone has ideas for tasks that I may be missing? Do I need to do anything to close a network connection other than closing it and setting it to nil? Also, I'm new to debugging tools – can anyone suggest a good tool to use to watch network connections and see what's causing them to fail?
Thanks!
Have you run it through the debugger (Cmd-Y)? Does it stop at the place where the crash is happening? That should show you in code where the issue is happening. I'm betting the issue has to do with over-releasing something rather than cleaning up connections. Are you getting EXC_BAD_ACCESS? Check any delegates and make sure they are nil when -viewWillDisappear gets called. That way, if anything tries to call back to a delegate, it will just be a no-op.
You may also want to try enabling zombies (NSZombieEnabled) which will tell you when an object that has been released is being accessed again. It's very helpful in finding over-released objects.
Ah ha... after a large zombie hunt (thanks, Matt Long), I discovered that the issue stems from an error in Apple's LazyTableImages sample code. That example provides the following implementation for canceling all image loads, which I turned into a general-purpose stopAllImageLoads method...
From RootViewController.m in LazyTableImages sample code:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// terminate all pending download connections
NSArray *allDownloads = [self.imageDownloadsInProgress allValues];
[allDownloads performSelector:#selector(cancelDownload)];
}
There is in error in the last line of the above method where performSelector is called on an array of objects. The above implementation calls the selector on the array itself, rather that on each object in the array. Therefore, that last line should be this:
[allDownloads makeObjectsPerformSelector:#selector(cancelDownload)];
Once that line was changed, everything else fell into place. It turns out I wasn't calling my stopAllImageLoads method where I meant to – I had disabled it at one point because it was causing an error. Once that was back in place, the memory issues cleared up because image loads were successfully canceled before the table delegate was released.
Thanks all for your help.
If you're doing ANY asynchronous function (network requests, Core Location updates, etc), you run the risk that your view controller that is the delegate of that action is deallocated by the time the async function returns. i.e. you back out of the view and take the delegate target away from the background process. I've dealt with this several times.
Here's what you do. Use the ASIHTTPRequest library (which you should be doing anyway--it's brilliant). Create a synthesized property to hold your request. Then in viewWillDisappear, call -cancel on your request. To be safe, I also set its delegate to nil, but that should be unnecessary.
Here's a sketch of what you want to do. Note I typed this right here, haven't syntax-checked it or anything.
#implementation MyViewController
#synthesize req //this is an ASIHTTPRequest *req.
-(void)viewDidLoad
{
//make an NSURL object called myURL
self.req = [ASIHTTPRequest requestWithURL:myURL];
self.req.delegate = self;
[self.req startAsynchronous];
}
-(void)viewWillDisappear
{
[self.req cancel];
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSString *string = [request responseString];
}
I've been using -performSelectorInBackground in many of my apps, sort of oblivious to -detachNewThreadSelector. Now I am wondering what the differences are between the two. Are they pretty much interchangeable, or are there differences and places where one is superior to the other? Thanks!
They're identical. See documentation.
performSelectorInBackground:withObject:
The effect of calling this method is the same as if you called the detachNewThreadSelector:toTarget:withObject: method of NSThread with the current object, selector, and parameter object as parameters.
As Michael says, they are identical. I think, to use a thread by performSelectorInBackground:withObject: is easier way rather than NSThread.
However, NSThread can control its priority, stacksize, etc. If you'd like to customize the behavior, I recommend NSThread instead of performSelectorInBackground:withObject:.
Corey, if you're running on a new thread and you're using [object autorelease], you'll need to create your own autorelease pool. Fairly simple to do:
- (void)uploadDataOnThread
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// do something here...
[pool release];
}
Interesting you guys can't point out any differences between them-
I'll tell you this much. We have an iphone app that was locking up while we displayed the "internet activity" disclosure icon in the top bar and a window that said "Sending..." while we sent an audio file up to a server and performSelectorInBackground: was NOT working. When we swapped out performSelectorInBackground with detachNewThreadSelector, it worked fine but now it complains that no autoreleased objects running in that thread are subject to being released by the pool.. maybe this is because the pool is in the main thread?
I am trying to dynamically add subviews to a UiScorllView in real time (to save memory).
Doing so causes the scrollview to hang for a brief second... what is the way around this?
any hints appreciated
Read up on the how the UITableView solves this problem. Your biggest performance hits here are the allocation and initial drawing of the subview, but primarily the allocation. UITableViews use a reuse identifier and an in-memory heap to keep all its cells in memory during scrolling so it can just reuse cells that have already been allocated instead of re-allocating new ones every time a new cell scrolls into the viewable area.
Perhaps you could implement your own subview reuse system to save yourself all that allocation time. Again, read up on specifically how the UITableView does it and model yours off of that. Unfortunately, there really is no easier solution than that that I can think of.
I suggest that any action that could possibly hang the user interface should be placed in a thread. This way, the process of adding the subview will be done in the background, not disturbing the main thread.
You could either do this by NSThread or you could implement the NSOperationQueue.
Declaration of the NSThread is simple;
[NSThread detachNewThreadSelector:#selector(myFunction:) toTarget:myObject withObject:argumentObject];
Where 'myFunction' should be replaced with the name of a function, the 'myObject' with an object (or simply state self if you want the current class to handle the thread) and 'argumentObject' should be replaced with any object you want to pass along as an argument; this may be nil
The function itself should look like this;
-(void)myFunction {
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
// do your thing here...
[pool release];
}
Every thread should have an autorelease pool.
One hint; if your subview has to animate, try declaring a UIAnimation block