I've set up an Objective-C category for an iPhone app's UIImageView class. The category's mission is to help load URL-based images asynchronously with memory/disk caching.
Now, in UIImageView+Cache.m I have access to an NSOperationQueue so I can kick off a loading thread. I create an NSOperation-derived object, initialized with the image URL and the target UIImageView, and a selector to perform on the target once the operation is complete. In the selector method, we set our freshly-loaded image (or, if not found, we set an alternate placeholder image), and we're done!
This works fine, until a UIImageView happens to be removed before the NSOperation completes. For instance, I have a previous/next segmented control in my UI that causes these UIImageViews to be removed and added anew (they're part of a larger "item" that is being viewed in the app), so it's very easy to tap these in rapid succession.
So if you decide to start tapping away before all the images are loaded - KABLAM! Unhappy thread has an invalid object and doesn't know it. :(
The closest thing I can find to help mitigate this is NSOperation's cancel and isCancelled methods, except you can't keep track of which operation object to cancel within a Category, because - if I understand correctly - Categories can't add IVARs to objects!
Maybe that means Categories aren't a good idea here? (Whines: "But I liiiiike Categories! Waaah!")
Advisement appreciated!
I probably wouldn't use a category for this situation. Categories are useful, but are usually unnecessary. I'd only use a category if you have a really good reason to. What exactly are you putting in the category?
I think you could implement the whole thing in the NSOperation subclass, which would be the best solution. Put a retain on the image view so it doesn't get deallocated before the image is downloaded, and cancel the download if the view is not visible anymore. If that's not possible, then subclass UIImageView instead of using a category.
I would say there is no harm in moving this into your own UIImageView subclass. Sure, you may like categories - but if they don't do the job then why hesitate in moving to a design that does?
Are you retaining the UIImageView by the NSOperation? Otherwise the imageView might be freed before the NSOperation completes, leading to kablooi central. You should do a retain and then, once you've done the setImage, do a release.
Related
I see many apps which have an image frame that loads the image asynchronously and while the image is not loaded yet, it shows either a spinner at the back or a default image. Is there a library for doing that? If yes then what is it? And please no three20 (I've had a bad experience with it). If not then how do you create one.
The spinner is called UIActivityIndicatorView, which is a subclass of UIView, and as such, can be placed anywhere on the screen.
It has startAnimating and stopAnimating methods, which begin and end the "spin".
So create the UIActivityIndicatorView, place it with addSubview where you want, and call startAnimating (this, as UI activity, has to be done on the main thread). Then call the image loading code asynchronously, and when the image finishes loading, call stopAnimating on the spinner, put the loaded image on top of it, and then remove the spinner with removeFromSuperview.
AFAIK, there is no built-in Cocoa API to do this in a call or two (which sounds like what you want).
I personally set up a singleton image cache manager that uses the built-in NSURLConnection class to load the images, save them to the filesystem, then call delegate methods on the requesting class (which includes a little subclass of UIImageView) to hand it the loaded image data.
People on SO report success with ASIHTTPRequest as well, for similar tasks. Worth checking out, I imagine (though I don't use it).
I'm creating an iPad game that has a viewController that loads in its view from a nib file. The view has a bunch of buttons in it, which I linked up to UIButton * variables via interface builder (so each button is linked to a different variable). I checked the retain count right after they nib was loaded on one of the buttons (using my first button variable, b1) and it gives me a value of 2. Can anyone explain why it's 2? What are the two things that are retaining it right after the nib loads?
And now I'm even more confused because in my dealloc function, I released each of the button variables individually, and checked the retain count for one of them after and it's STILL 2! It should at least have gone down to 1, shouldn't it have? Should I release it several times in my dealloc function? If so, how many?
Thanks
Don't look at retain count.
Seriously.
Things other than you retain your stuff. Those numbers will move around underneath you for reasons that appear to make no sense, and then you'll come back here and post bewildered questions.
Just make sure your retains and releases balance. That's your only job.
If your IBOutlet properties are retained then you would have 1 retain there and another when the button gets added to the super view...
As Dan Ray says though, you shouldn't really worry about the retainCount...
I know and heard most UI related shouldn't be done in thread other than main.
I also know that you can update non-view related data member(that you added) of UIView derived class.
I wonder if the below operations are fine or not to do in background thread.
allocing UIView
init UIView with/without(CGRectZero or just init) frame info
modifying frame/image(UIImageView's) property of UIView
modifying image property of NSObject derived class. (treating UIImage as data)
accessing subviews with subviews method
etc.. Is there a well defined documentation on this issue?
Thank you
You shouldn't be doing anything view related in a background thread. All of the items you listed should not be done in a background thread. If you're breaking your app up correctly for MVC, the view should only contain items that dictate how it is displayed. So anything relating to one should only be in the main thread.
All of your data manipulation should be residing in your model. It can be threaded as needed for performance. Just be careful that you send any messages to update the UI for the data manipulation on the main thread. This includes notifications. They gets sent on the same thread they were created on. So it's easy to forget to switch into mainThread when sending one.
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.
I have a UIImage that I use throughout my app as a background image for grouped UITableViews.
I thought that for efficiency I would alloc and init the UIView with my UIImage in my appDelegate and then access throughout my app. That way I would only allocate that imageView once and if I was drilling into a nav stack with multiple tableviews with this image I wouldn't need to worry about releasing and restoring the image as I descend and ascend or incur overhead at each step.
As soon as I tried this I noticed that it seems that the UITableView class is releasing the my shared image down to 0 and it therefore is going away. Makes perfect sense but I would need to prevent the image from ever hitting a 0 retain count for this to work.
Is this a totally goofy approach?
If it is not what's the best way to retain my shared ImageView? I know I could call retain when I setup each tableview's backgroundimage but I was wondering if there is a way to set the retain count of the shared UIImageView to NSUIntegerMax in my appDelegate. I've setup singleton classes before but in this case I'm trying to have a single property that is never released rather than creating a UIImageView singleton subclass.
Sorry if that's a little muddled and thanks for any pointers.
I would not worry so much as + (UIImage *)imageNamed:(NSString *)name are cached.
From the spec:
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.