showInView: method for custom UIView - iphone

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.

Related

Modal View doesn't release itself after dismiss

I've been facing a weird issue with dismissing a modal view.
I present a modal view like this:
ResepiDetail *detail =(ResepiDetail*)[[ResepiDetail alloc]init];
[self presentModalViewController:detail animated:YES];
and dismiss it like this with a back button:
[self dismissModalViewControllerAnimated:YES];
after this the view dismisses itself and goes back to the previous view, but it doesn't release itself from memory. I found it out by sending a notification message and that view received it. Additionally I tried to track the VM memory Allocation, and it seems the view is still in memory.
I'm using ARC and have the same method used for another view which works perfectly.
The code is fine, as posted, so here's some hints on how to proceed:
A sure fire way to be certain your view controller hasn't been deallocated is to override dealloc and log something identifiable. You can still do that in ARC, just don't explicitly call super. If you don't see the log when you expect to, then you have a problem.
Assuming that you have determined that you absolutely do have a problem, then the issue becomes finding the retain cycle. If the issue is that an instance of ResepiController isn't being dealloc'd, then you need to look for...
Any code outside the ResepiController class that has a strong reference to it. For example, if your class signs up as the delegate of some other class, make sure the delegate isn't using a strong reference.
Any internal blocks that may have implicitly retained self. Are there any blocks anywhere in your program that may have a reference to your controller at the time that you think it should be released?

three20 - TTTableViewController Memory warning gives blank screen, how to fix?

This is driving me nuts. I am using three20's TTTableViewController and when I get a memory warning, the screen goes white. Now, after reading on the three20 google group is seems that the tableView got released. But, I cannot for the life of me figure out a check to see if that is the case, then create it again.
I was using the following because I thought it would fix the issue, but it seems that it doesn't satisfy the if statement:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// If we don't have a datasource we need to reset it
if (!self.dataSource) {
// Create datasource again
}
}//end
Does anyone know what to do when this happens? The google group has been no help.
Are you subclassing TTTableViewController? I haven't used it before, but assuming it's just like UITableViewController...
How does your "viewDidUnload" look like? Are you releasing the tableview here? If so, you need to create tableview in viewDidLoad to match it.
No need to check if dataSource is available in viewDidAppear, because if you read View programming guide, it explains that memory warning will call "viewDidUnload" to give you a chance to clean up data that are created in "viewDidLoad".
i had the same issue and it drove me crazy as well.
Nobody mentions it in the three20 docs, but you shouldn't use UIViewController's initWithNibName function to add subviews. If you do, a memory warning will release these subviews.
Try to move your code from initWithNibName function to viewDidLoad function. I have noticed that some code need to be kept in the initWithNibName, such as navigation styles. However, any subviews added to the controller's view should be in the viewDidLoad function.
In general you should be careful to set up views in viewDidLoad rather than the class constructor. For instance, you should set up your launcher view in viewDidLoad rather than the constructor of your launcher view controller, otherwise your launcher will become empty after a memory warning.
In the case of TTTableViewController however this does not (usually) apply because you don't set up the table view manually. I had the same problem you had, and eventually tracked it down: I had redefined viewWillDisappear: and forgot to call [super viewWillDisappear:animated]. This meant that some of the flags that the Three20 controller maintains about the state of the view were not updated correctly.
I also found that it was beneficial to redefine didReceiveMemoryWarning to call [self setEditing:NO] before calling super; I found that the state of the table view got confused otherwise (this is not relevant if you don't use edit mode for your table).
Finally, there is a bug in Three20 which means that tables in loading/empty/error mode will not be restored properly; see a discussion in the blog post by TwoCentStudios and a proposed fix on github.

Is there a leak in the Scroll View Suite sample code by Apple?

I need help with the code from Apple's ScrollViewSuite; specifically I'm looking at the ThumbImageView class, which doesn't have a dealloc method defined. I see in the .h that the property for imageName uses retain.
(I wasn't sure if I was allowed to post any code since it's Apple's, so please let me know if I can/should.)
Anyway, I thought if we use "retain" that we are responsible for releasing the object reference.
The method CreateThumbScrollViewIfNecessary (from the RootViewController implementation file) has a for loop which allocs ThumbImageViews, sets the delegate, and then after adding the thumbview as a subview the a scrollview, proceeds to release the thumbview. If these objects are actually being released, how does the delegate do its job notifying when an image has been tapped, scrolled, etc.
Sorry I'm just so confused. Any help would be greatly appreciated.
The code leaks. Unfortunately, Apple’s sample code usually leaves a lot to be desired, the design often sucks and there are leaks and glitches. It’s best to take it only as an annotated API reference that shows how various parts of the API fit together, nothing more.
You are responsible for clearing the object reference. As far as I can tell, that code of Apple's would leak if that property were ever assigned a value.
Any view retains its subviews. After each view has been added to the scrollview, the class that creates it has no more use for it so it releases its reference. The object won't actually be deallocated until the scrollview also releases its reference, so the views remain "live" and able to signal their delegates until that happens.

Stop lazy-loading images?

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];
}

Adding subview, gets delayed?

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];
}