2

Given the example code below:

// ExampleModel.h

@interface ExampleModel : NSObject <ASIHTTPRequestDelegate> {

}

@property (nonatomic, retain) ASIFormDataRequest *request;
@property (nonatomic, copy) NSString *iVar;

- (void)sendRequest;


// ExampleModel.m

@implementation ExampleModel

@synthesize request;
@synthesize iVar;

# pragma mark NSObject

- (void)dealloc {
    [request clearDelegatesAndCancel];
    [request release];
    [iVar release];
    [super dealloc];
}

- (id)init {
    if ((self = [super init])) {
        // These parts of the request are always the same.
        NSURL *url = [[NSURL alloc] initWithString:@"https://example.com/"];
        request = [[ASIFormDataRequest alloc] initWithURL:url];
        [url release];
        request.delegate = self;
        [request setPostValue:@"value1" forKey:@"key1"];
        [request setPostValue:@"value2" forKey:@"key2"];
    }
    return self;
}

# pragma mark ExampleModel

- (void)sendRequest {
    // Reset iVar for each repeat request because it might've changed.
    [request setPostValue:iVar forKey:@"iVarKey"];
    [request startAsynchronous];
}

@end

# pragma mark ASIHTTPRequestDelegate

- (void)requestFinished:(ASIHTTPRequest *)request {
    // Handle response.
}

- (void)requestFailed:(ASIHTTPRequest *)request {
    // Handle error.
}

When I do something like [exampleModel sendRequest] from a UIViewController, it works! But, then I do [exampleModel sendRequest] again from another UIViewController and get:

Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: '*** -[NSOperationQueue addOperation:]:
operation is finished and cannot be enqueued`

How can I fix this?

ma11hew28
  • 121,420
  • 116
  • 450
  • 651

3 Answers3

6

You shouldn't attempt to reuse the request object. It maintains state. Really designed to be disposed off after the request is over.

The design isn't as clean as the NSURLConnection, NSURLRequest, NSURLResponse classes (basically mashing all three into one and wrapping the low level core foundation classes underneath). It's still far better than using NSURLConnection in a vanilla fashion if you need to deal with low level HTTP stuff. If you don't, the high level classes have some advantages (like access to the same cache the UIWebView uses).

Zac Bowling
  • 6,508
  • 1
  • 27
  • 26
  • 1
    Consequently, I would not recommend having your request as an instance variable since it is designed to be disposable. If you want the model object to make the request, but trigger it from a controller (which is what I gather you're wanting to do), better to send a notification to the model from the controller and have the listener invoke sendRequest. You can still pass in stuff from the controller, but you are less likely to have problems. – Alec Sloman Jun 03 '11 at 01:58
  • @Alec Sloman, so, if I make the request a local variable and release it in `requestFinished` & `requestFailed` callbacks, then what happens if the model is `dealloc`ed before either of these callbacks are called? Then, the request will never get dealloc`ed, correct? Isn't that a potential memory leak? – ma11hew28 Jun 03 '11 at 02:26
  • @MattDiPasquale Yes, that's exactly right. But I'm not sure why you would allow your model to be released before your request was completed. You can query the request to see if it has completed. You might consider handling that if expect your model to be released in the way you described. – Alec Sloman Jun 03 '11 at 04:02
  • there is a [request clearDelegatesAndCancel] function if you want to dealloc your model. I set my request to an instance variable, but I set it to nil in the requestFinished/requestFailed callbacks (because it will dealloc on it's own) and then I call [request clearDelegatesAndCancel] in my own dealloc (which if it's nil won't go anywhere). I retain it in one case. One issue is that ASIHTTPRequest will crash if the delegate is released and it isn't niled (it doesn't retain it for obvious reasons). With blocks you need to use the same care. – Zac Bowling Jun 03 '11 at 05:56
2

I think I found the answer: https://groups.google.com/d/msg/asihttprequest/E-QrhJApsrk/Yc4aYCM3tssJ

ma11hew28
  • 121,420
  • 116
  • 450
  • 651
1

ASIHTTPRequest and its subclasses conform to the NSCopying protocol. Just do this:

 ASIFormDataRequest *newRequest = [[request copy] autorelease];
 [newRequest startAsynchronous];
Alexsander Akers
  • 15,967
  • 12
  • 58
  • 83