Reputation: 779
I'm using RestKit to download several files in my app. As long as the files aren't too big everything works like a charm. Unfortunately I've tried downloading a big video file (around 230 MB) and get a memory warning
and the app crashes.
I start the download with the following line
[[RKClient sharedClient] get:[NSString stringWithFormat:@"/%@", listItem.filename] delegate:self];
In the delegate method I save it localy in appDirectory/Library/Caches/
- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response
{
NSURL * url = [NSURL fileURLWithPath:[cacheDirectory stringByAppendingPathComponent:response.URL.lastPathComponent]];
[[response body] writeToURL:url atomically:YES];
}
I've checked the app in Instruments for memory leaks and saw that Live Bytes
for *Overall Allocations*
kept rising up before the Low Memory Alerts
came at around 100 MB
. There were no leaks though.
Removing the line that's responsible for saving the file ([[response body] writeToURL:url atomically:YES];
) didn't change anything, so I guess the problem is somewhere in RestKit.
While researching I stumbled upon a method of ASIHTTPRequest
which allows direct file download and thought something like that could solve the issue, not sure though.
Is there possibility in RestKit for direct downloading that I missed?
or
Is there a better way to download files with RestKit that I should know of?
Upvotes: 0
Views: 2379
Reputation: 7344
I encountered the same problem, unfortunately I don't think there is a way around other than modifying your local RestKit copy. The problem is located in - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
, exactly in [_body appendData:data];
. As you can see, RestKit keeps the body of the response in the memory and appends the bytes as they are sent from the server. This is why you get low memory warnings. My solution was to add a property to RKRequest and write data directly to file system depending if the request is a big file request or not:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if ([_request dontKeepBytesInMemory]) {
if ([[_request delegate] respondsToSelector:@selector(request:didReceivedData:totalBytesExectedToReceive:)]) {
[[_request delegate] request:_request didReceivedData:data totalBytesExectedToReceive:_httpURLResponse.expectedContentLength];
}
} else {
[_body appendData:data];
if ([[_request delegate] respondsToSelector:@selector(request:didReceivedData:totalBytesReceived:totalBytesExectedToReceive:)]) {
[[_request delegate] request:_request didReceivedData:[data length] totalBytesReceived:[_body length] totalBytesExectedToReceive:_httpURLResponse.expectedContentLength];
}
}
}
I also needed additional RKRequest delegate method in the protocol, so I could process the big responses separately. The original delegate method defines totalBytesReceived
which is simply the [_body length]
, but if you write bytes directly to file system you can just check the current file size to track the download status. If you are looking for a good way to save bytes directly to file system, take a look at NSFileHandle class - it supports file offset so you can just throw any NSData at it and it will automatically append it for you.
Upvotes: 4