Best practices, fire-and-forget asynchronous classes? - iphone

I have a class whose sole purpose is to go download a specific file off the net, store it locally and then return the local path of the stored file.
I use this class based on whether I have a local copy of the file in question or not, and I sometimes call it up multiple times at the same time, if more than one file needs downloading. The way I use it is simply
Loader *l = [[Loader alloc] initWithDelegate:self];
[l downloadFile:someFile];
[l release];
The thing is, in order to keep it around until it's done downloading, I am actually doing [self retain]; in the class, then [self autorelease]; when it's done. This feels hacky though. How do people deal with this?

I agree that [self release] and [self autorelease] feel weird, but that doesn't mean they're wrong. In some situations, they can be the right thing to use (and I've used them before).
If you want to avoid them, however, you might consider making a LoaderManager class that simply owns the Loader objects until they're done loading stuff. It'd essentially be a wrapper around an array, and you'd use it like this (or something):
#interface LoaderManager : NSObject {
NSMutableSet *loaders;
}
#end
#implementation LoaderManager
- (id)init {
self = [super init];
if (self) {
loaders = [[NSMutableSet alloc] init];
}
return self;
}
- (void)dealloc {
[loaders release];
[super dealloc];
}
- (void)addLoader:(Loader *)loader {
[loaders addObject:loader];
}
#end
And then your Loader object would do:
[myLoader downloadFile:someFile manager:aLoaderManager];
Which internally would simply invoke:
[aLoaderManager addLoader:self];

Under the circumstances, I think it's fine for your Loader to retain and autorelease itself. The most expedient solution might be to just add a detailed comment to your code that explains why it does what it does. The biggest problem is that Loader takes a delegate. Delegators don't usually retain their delegates in order to avoid retain cycles, but in this case it seems possible that the delegate could be deallocated before the Loader is finished downloading its file. If that happens, a crash is likely. So, if you want to continue with this fire-and-forget style, you might want to have Loader retain its delegate.

Related

Confused on using Class method with an object that has a delegate

I'm confused on Delegates. I was always under the impression that if you didn't set a delegate than it wouldn't respond to delegate callbacks. I have a crash with a Class method that releases its object.
Lets say I have a object "Something" that has a delegate set-up for handling results to the callers that care. Something uses ASIHTTPRequest to do some posts / gets asynchronously.
-(void)somethingHasResults{
//something needs to tell its listeners that its has completed (pseudo-code)
if([delegate respondsToSelector:#selector(somethingDidComplete)]){
[delegate somethingDidComplete];
}
}
But I also have a Helpers.h/.m with a series of class methods like so...
+(void)shoutItOutLoud{
//doesn't need a delegate, doesn't need a response -- just do your thing and exit
Something *something = [[Something alloc] init];
[something shouldDoSomethingAwesomeButDoesntNeedToRespond];
[something release];
}
When I use [Helpers shoutItOutLoud] I get a crash. Now the crash is actually in ASIHttpRequest.m's reportFinished, but following the trace brings me all the way back to my class method which is releasing the object before completion.
The whole point of this is that I'm surprised that I have a crash here and I'm trying to wrap my head around this. A co-worker and I got into a discussion and he says its because I'm releasing my object so the delegate is pointing at garbage. This seems wrong to me, as this class method doesn't receive the responses anyways?
I believe your problem is actually to do with not cleaning the ASIHttpRequest delegate: an ASIHttpRequest object sets its delegate to the object is called from.
So you will need to clear that delegate as well (in the Something class). Assuming that you have a property of type ASIHttpRequest, here is what I do in the dealloc method:
- (void)dealloc {
...
[_request clearDelegatesAndCancel];
[_request release];
[super dealloc];
}

Can I skip creating a separate pointer when setting my viewController?

Why do I need to create a pointer just to allocate memory and then release it immediately?
In other words, can't I just do this:
self.viewController = [[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController" bundle:[NSBundle mainBundle]];
instead of this:
HelloWorldViewController *newViewController = [[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController" bundle:[NSBundle mainBundle]];
self.viewController = newViewController;
[newViewController release];
[EDIT]
To provide wider context on my question: (within #implementation of #interface HelloWorldAppDelegate : NSObject <UIApplicationDelegate>)
#synthesize window=_window;
#synthesize viewController=_viewController;
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
HelloWorldViewController *newViewController = [[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController" bundle:[NSBundle mainBundle]];
self.viewController = newViewController;
[newViewController release];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
...
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
So, within the didFinish... method, can I use the short version I supplied way above, or would it leak?
[SECOND EDIT]
Seems I just can't give enough info. I'm always hesitant to post a huge pile of code.
I've got this in HelloWorldAppDelegate.h:
#property (nonatomic, retain) IBOutlet HelloWorldViewController *viewController;
So, correct me if I'm wrong. Given the declaration of viewController in the header file, if I use the shortcut method (in the first code snippet above), the program will leak. The object counter is incremented once by the alloc, and then a second time by the retain, then decremented once by the release, producing a net of +1 on the pointer reference counter.
You do have to release the object that you allocated, or you will end up with a memory leak. The assignment to self.viewController (presumably) retains the newly-alloc'd HelloWorldViewController, so you have two "claims" on the object -- one from your call to alloc and one from the retain, but you're not actually going to use it any more under the name newViewController, so you relinquish that particular claim by calling release.
The "long" form, using the temp variable, is in fact the correct way to do this. Sending release to the result of the property access: [self.viewController release]; right after it's set is quite likely to work, but is incorrect (and will generate a compiler warning for recent LLVM versions).
It's also possible to do this:
self.viewController = [[[HelloWorldViewController alloc] initWithNibName:#"HelloWorldViewController"
bundle:[NSBundle mainBundle]]
autorelease];
which says "I've created this thing to use it briefly, but I won't need it outside this immediate call stack, so go ahead and release it for me later."
If viewController isn't a retaining property, then all this is moot, and you should not be sending release to the new view controller, because it will then be deallocated before you can use it.
UPDATE: Your expanded question doesn't change anything*. If you don't send release before newViewController goes out of scope, you will end up with a leak.** The way this works is, any object (A, B, C) that needs to use object X, and therefore cares about keeping it around, sends retain to X. When you alloc an object, it's assumed that you need to use it. When any of A, B, or C no longer need X, it sends release to X, thereby saying "X can be deallocated and it won't affect me". You need to balance the number of claims you make (by using retain, alloc, copy/mutableCopy, or new) on an object with the number of relinquishments you make (by sending release or autorelease) or you will have either a leak or an accidental deallocation on your hands.
I'm going to give you a link to the docs now, but don't be annoyed, it's just one page that you need to read and internalize: The Fundamental Rule of Cocoa Memory Management.
*Actually, you left out the only part that could've changed the answer, namely your
#property () HelloWorldViewController * viewController;
declaration. If it says "retain" inside those parentheses, then when this property is set, your app delegate is sending retain to the passed object. If it says "assign", or the parentheses aren't there, then, like I said, you should not send release to that object.
**Note for any pedants looking on: you could of course send release twice to the viewController at some point, but that's worse than a leak.
Yes you can do this. Except you have to release self.viewController so that the retain count is 1 by the end of the snippet.
Let's count. Upon allocation, the release count is 1. Assiging it to self.viewController (assuming it's a property with retain behavior) increases it to two. Releasing it at the end of the second snippet makes it 1 again. The idea is that you'll release it completely in the dealloc of the current class, whatever that is.
The only wrinkle is that releasing an object variable assumes that you're done with it, and here you have to go, releasing self.viewController but using it later. Kinda smelly.

Objective-C memory management - pretty sure I'm doing this all wrong

After 3 hours or so, I've finally managed to fix a memory leak in a view controller. The leak was caused by a UIPickerView that has its property set to 'retain' in the header file.
The following code managed to fix it:
- (void)viewDidLoad {
[super viewDidLoad];
myPicker = [[[UIPickerView alloc] initWithFrame:CGRectZero]autorelease];
}
- (void)dealloc {
[super dealloc];
[myPicker release];
myPicker = nil;
}
Please don't tell me how shocking this code is... I know it's bad. I've got a release, and an autorelease. Problem is, if I change or remove any part of the above, the memory leak returns.
I though I knew how objective C memory management works, obviously not...
Why does the above code fix the memory leak, and what might a correct version of the code look like?
-
EDIT:
If anyone has the same problem, or is interested - the problem was that one of the other objects in my class was set to 'retain' rather than 'assign'. (If you don't own an object, it should have the property assign, not retain).
Like Cannondale said, removing the extra retain fixes everything, and only one release is necessary.
You must be doing a retain on myPicker somewhere else in your code. Your myPicker allocation line will release that memory as soon as the stack unrolls for the viewDidLoad call (that is what the autorelease is telling it do do).
You must be doing a retain somewhere after that point, if not then your [myPicker release] will be trying to free unallocated memory with unpredictable results.
What you should be doing is allocating the memory in viewDidLoad (so remove the autorelease). Make sure you don't retain the object anywhere else and release myPicker from the dealloc.
Also ... what bbum said re the dealloc ;)
What Cannonade said. This should work:
myPicker = [[UIPickerView alloc] initWithFrame:CGRectZero];
Your dealloc is busted, too. Call to super always has to be last (think about it) and that can lead to undefined behavior.
- (void)dealloc {
[myPicker release];
myPicker = nil;
[super dealloc];
}

NSMutable Array and brain damage

I have understandably attracted some smart ass answers to some bone head questions I have asked lately due to my complete misunderstanding of obj c and NSMutable arrays. So I've looked in a book I have and Google and it would appear that may be I shouldn't program because I'm still stuck.
Say I'm looping through method A and each loop through I want to add a double into a NSMutable array. This is what I've discovered so far.
You can't put primitives in arrays, so I made my double into a NSNumber object
NSNumber *newObject = [NSNumber initWithDouble:doubleNumber];
XCode warns: 'NSNumber' may not respond to '+initWithDouble:'
and when I compile it get:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[NSNumber initWithDouble:]: unrecognized selector sent to class 0x1ffea0'
I ignore that and forge on. Some one told me I need an init method for the array, which makes sense because I don't want that happening in the loop. This is my init method:
-(id) init{
[super init];
arrayOfTouches = [[NSMutableArray alloc] init];
return self;
}
I haven't got to try to add it to the array yet.
[arrayOfTouches addObject:newObject];
Will this work to print out the array.
NSLog(#"array: %#", touchBeginObj);
Bonehead questions, probably. But damn if I can find anything that has all the pieces about what you can and can't do with an good example.
Thanks for any help or smart ass comments.
Yes, I have a Obj-C book, 3 actually and yes I'm reading them :)
Have a good weekend.
NSNumber *newObject = [NSNumber initWithDouble:doubleNumber];
You’re missing the +alloc call in the above code, which is why it’s throwing that error—but why not just use:
NSNumber *newObject = [NSNumber numberWithDouble:doubleNumber];
It’s autoreleased, so you don’t have to worry about releasing it later.
I’m not sure what either of the other questions are asking, could you possibly rephrase them? From the code you’ve given, I think it should work
First :
[NSNumber numberWithDouble: double]
instead of
[NSNumber initWithDouble: double]
Your addObject looks fine.
You have several ways to print your array, I'd suggest iterating, as it will be useful later:
for (NSNumber *number in arrayOfTouches)
{
NSLog(#"Number %f", [number doubleValue]);
}
initWithDouble is an instance method, you're looking for the class method numberWithDouble.
Change this:
NSNumber *newObject = [NSNumber initWithDouble:doubleNumber];
to this:
NSNumber *newObject = [NSNumber numberWithDouble:doubleNumber];
-initWithDouble is an instance method of NSNumber (so you would need to do [NSNumber alloc] first). +numberWithDouble is a class method.
You need to have zero warnings from the compiler - you can't ignore them and 'forge on'.
You are struggling with the basics, do you have experience of other programming languages?
There are a few things you must know in order to code in Objective-c and you must be familiar with the general concepts of object-oriented programming. Also a little C can be helpful as objective-c is an extension to C and it can give some perspective as to why object orientation is so useful and worth-the-effort.
If you already know a language like Python, Java, or even Actionscript then pretty much all of the concepts are the same and objective-c really isn't that difficult. You just need the specifics on how those concepts translate into objective-c. For example,
How do you define a class? How do you override an existing class? What does an initializer method look like? How do you create an instance of a class? etc. There are a few more things than this, but really not that many - you need to learn them, or at least have references at hand in the beginning so you don't go wrong. If you don't know what they mean then you need to learn that.
A 'Class' is like a blueprint for the layout of a piece of memory, in the case of NSNumber a piece of memory that we would hope is going to hold a number. The NSNumber Class knows how big the memory has to be and how it should be laid out. We really care little about the blueprint, the Class - we want an actual chunk of memory that can hold a number - an instance of the Class.
In objective-c creating an object, an instance, of a given class always, everytime, always, always involves -- firstly -- claiming ownership of a free chunk of memory with the correct size, saving the address of that chunk of memory to a variable, then giving the object a chance to do it's own initial setup by calling it's preferred setup method.
To claim ownership of the memory you send the alloc method to the class.
NSNumber *myNumberVariable = [NSNumber alloc];
To set the number up with the initial value of 10.0 you send the message initWithDouble: to your new instance.
[myNumberVariable initWithDouble:10.0];
For convenience these can be, and usually are combined into one statement.
NSNumber *myNumberVariable = [[NSNumber alloc] initWithDouble:10.0];
This is the most fundamental aspect of objective-c programming. You cannot do anything without thoroughly understanding this. Googling init objective-c returns literally hundreds of tutorials, guides, cheat-sheets and help.
If your own Class needs a property initializing when an instance is created, like your arrayOfTouches, you must override the preferred setup method of the parent Class. If the parent Class is NSObject, NSObject's preferred setup method is init. If you create an object in your setup method you must always have a dealloc method. This will always, always, all of the time look like this:
- (id)init {
self = [super init];
if(self){
}
return self;
}
- (void)dealloc {
[super dealloc];
}
Only the name "init" will change, depending on the name of the method you are overriding, - (id)init becomes - (id)nameOfMethodIAmOverriding and [super init] becomes [super nameOfMethodIAmOverriding];
Given this standard template for setup and teardown you can add in the setup and teardown of your own properties. In your case giving you.
- (id)init {
self = [super init];
if(self){
arrayOfTouches = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
[arrayOfTouches release];
[super dealloc];
}
Look, it's not that i think you have Boneheaded questions, they are sensible questions but if you have 3 books - the answers are almost certainly on the first few pages of each book - maybe you are going about learning this wrong. Somehow you are not getting the fundamentals. The best i can suggest is to go more slowly and do the exercises in the book, Or choose a simpler language and attempt to master that before moving to objective-c.
Go to Help-->Developer Documentation
Search for NSNumber. Under class methods you'll find how to create a NSNumber with a double.

Is it better to autorelease or release right after?

There are a lot of cases in which one would alloc an instance, and release it right after it's being assigned to something else, which retains it internally.
For example,
UIView *view = [[UIView alloc] initWithFrame...];
[self addSubView:view];
[view release];
I have heard people suggesting that we go with autorelease rather than release right after.
So the above becomes:
UIView *view = [[[UIView alloc] initWithFrame...] autorelease];
[self addSubView:view];
What's the best practice here? Pros and cons?
In most cases, it wont really matter either way. Since -autorelease simply means that the object will be released at the end of the current iteration of the run loop, the object will get released either way.
The biggest benefit of using -autorelease is that you don't have to worry about the lifetime of the object in the context of your method. So, if you decide later that you want to do something with an object several lines after it was last used, you don't need to worry about moving your call to -release.
The main instance when using -release will make a noticeable difference vs. using -autorelease is if you're creating a lot of temporary objects in your method. For example, consider the following method:
- (void)someMethod {
NSUInteger i = 0;
while (i < 100000) {
id tempObject = [[[SomeClass alloc] init] autorelease];
// Do something with tempObject
i++;
}
}
By the time this method ends, you've got 100,000 objects sitting in the autorelease pool waiting to be released. Depending on the class of tempObject, this may or may not be a major problem on the desktop, but it most certainly would be on the memory-constrained iPhone. Thus, you should really use -release over -autorelease if you're allocating many temporary objects. But, for many/most uses, you wont see any major differences between the two.
I agree with Matt Ball. Let me just add that, if you find yourself using this pattern frequently, it can be handy to write a quick category:
#interface UIView (MyCategories)
- (UIView *)addNewSubviewOfType:(Class)viewType inFrame:(NSRect)frame;
#end
#implementation UIView (MyCategories)
- (UIView *)addNewSubviewOfType:(Class)viewType inFrame:(NSRect)frame
{
UIView * newView = [[viewType alloc] initWithFrame:frame];
[self addSubView:newView];
return [newView autorelease];
}
#end
Which can be used as follows:
UIView * view = [someView addNewSubviewOfType:[UIView class]
inFrame:someFrame];
And it even works with other types, as long as they are derived from UIView:
UIButton * button = [mainView addNewSubviewOfType:[UIButton class]
inFrame:buttonFrame];
I usually go for -release rather than -autorelease whenever possible. This comes from years of experience debugging and enhancing other people's Objective-C code. Code that uses autorelease everywhere makes it harder to debug when an object gets over-released, since the extra release happens far away from the incorrect code.
It's also the case that many folks use autorelease when they just don't understand how cocoa memory management works. Learn the rules, learn the API, and you'll almost never need to autorelease an object.
A last minor point is that if you don't need the autorelease behavior, then using autorelease just needlessly adds extra work for your program to do.