Need to call retain for a property with retain flag specified - iphone

I'm newbie in Objective-C, and as most of the newbies I have a questions about references management.
I've written a class which downloads data using NSURLConnection. The code is similar to Apple's example in http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html. The only difference is that receivedData variable is declared as "#property (nonatomic,retain) NSMutableData *receivedData;" In .m file I have "#synthesize receivedData = _receivedData;".
I have connectionStart function which starts downloading data. In this function I have this code:
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
self.receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed.
}
The program crashes with this message:
2011-06-12 12:47:22.298 WebGallery[1212:207] *** -[NSConcreteMutableData release]: message sent to deallocated instance 0x118a6fe0
If I change receivedData assignment to this code:
self.receivedData = [[NSMutableData data] retain];
Then the program works correctly and no memory leaks are detected.
As you see I need to call retain on NSMutableData and I'm using property, which is declared as "retain".
Why does this happen?
EDIT: Full contents of .m file:
#import "GalleryData.h"
#import "XmlParser.h"
#implementation GalleryData
#synthesize receivedData = _receivedData;
#synthesize imagesData = _imagesData;
#synthesize delegate = _delegate;
#synthesize currentObjectType = _currentObjectType;
#synthesize currentObjectIndex = _currentObjectIndex;
- (id) init
{
[super init];
_imagesData = [[NSMutableArray alloc] init];
return self;
}
- (void) dealloc
{
[_imagesData release];
_imagesData = nil;
[super dealloc];
}
- (void) connectionStart:(NSURL *)theURL
{
NSURLRequest *theRequest = [NSURLRequest requestWithURL:theURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
//ASK: Kodėl čia reikia daryti retain jei #property jau nustatyta retain?
self.receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
}
- (void) startLoading
{
NSLog(#"Loading started");
self.currentObjectIndex = 0;
self.currentObjectType = ObjectTypeXML;
[self connectionStart:[NSURL URLWithString:#"http://www.aleksandr.lt/gallery/data.xml"]];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection release];
[self.receivedData release];
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
if (self.currentObjectType == ObjectTypeXML) {
NSXMLParser *nsXmlParser = [[NSXMLParser alloc] initWithData:self.receivedData];
XmlParser *parser = [[XmlParser alloc] initXmlParser:self.imagesData];
[nsXmlParser setDelegate:parser];
[nsXmlParser parse];
[nsXmlParser release];
[parser release];
[self.receivedData release];
self.receivedData = nil;
if ([self.imagesData count]) {
self.currentObjectIndex = 0;
self.currentObjectType = ObjectTypeThumbImage;
ImageData *firstImage = [self.imagesData objectAtIndex:0];
NSURL *theURL = [NSURL URLWithString:firstImage.thumbImageURL];
[self connectionStart:theURL];
} else {
[self.delegate loadingFinished];
return;
}
} else if (self.currentObjectType == ObjectTypeThumbImage) {
ImageData *currentImage;
currentImage = [self.imagesData objectAtIndex:self.currentObjectIndex];
UIImage *thumbImage = [[UIImage alloc] initWithData:self.receivedData];
if (thumbImage == nil) {
NSLog(#"image was not created");
}
[currentImage setThumbImageScaled:thumbImage];
[thumbImage release];
[self.receivedData release];
self.receivedData = nil;
if (self.currentObjectIndex == ([self.imagesData count] - 1)) {
[self.delegate loadingFinished];
return;
}
self.currentObjectIndex++;
currentImage = [self.imagesData objectAtIndex:self.currentObjectIndex];
NSLog(#"'%#'", currentImage.thumbImageURL);
NSURL *theURL = [NSURL URLWithString:currentImage.thumbImageURL];
[self connectionStart:theURL];
}
}
#end

Do not call [self.receivedData release] - this leaves the internal pointer dangling. The whole point of a retained property is that it releases itself. Just do self.receivedData = nil.

Here is your problem:
[self.receivedData release];
self.receivedData = nil;
You're releasing the attribute twice (first time explicitly and second time implicitly by assigning nil).

Related

Calls to NSURLConnectionDataDelegate methods to download images only works occasionally

I currently have two UIImageViews in my storyboard, one of which downloads my own Facebook profile picture, and the other a friend's profile picture.
However, my issue is that only 60% of the time this works as expected, while the other 40% of the times my own profile picture appears where my friend's picture should show in the bottom, while the top box remains empty. I am unsure whether this is the result of how I call the NSURLConnectionDataDelegate methods as it downloads or completes view, or the nature of my requests called to Facebook.
I've pasted the condensed version of my two requests to Facebook in viewDidLoad, one which gets my own profile pic, and the other to get my friend's pic:
// ----- Gets my own profile picture, using requestForMe -----
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
//handle response
if(!error){
//Some methods not included for breveity, includes facebookID used below
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
self.imageData = [[NSMutableData alloc] init];
switcher = 1;
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:pictureURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:2.0f];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!urlConnection){
NSLog(#"Failed to download picture");
}
}
}];
// ----- Gets a profile picture of my friend, using requestForMyFriends -----
FBRequest *requestForFriends = [FBRequest requestForMyFriends];
[requestForFriends startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error){
//Other methods not included, including facebookFriendID
NSURL *friendPictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookFriendID]];
self.supportImageData = [[NSMutableData alloc] init];
switcher = 2;
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:friendPictureURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:2.0f];
NSURLConnection *urlConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
if (!urlConnection){
NSLog(#"Failed to download picture");
}
}
}];
Both requests make calls to the NSURLConnectionDataDelegate methods, and I use the switcher to decide when to load which picture:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// As chuncks of the image are received, we build our data file
if (switcher == 1) [self.imageData appendData:data];
if (switcher == 2)[self.supportImageData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//When the entire image is finished downloading
if (switcher == 1) {
UIImage *image = [UIImage imageWithData:self.imageData]; //puts the completed picture into the UI
self.titleImageView.image = image;
[self.titleImageView setClipsToBounds:YES];
}
if (switcher == 2) {
UIImage *supportImage = [UIImage imageWithData:self.supportImageData];
self.supportImageView.image = supportImage;
[self.titleImageView setClipsToBounds:YES];
}
}
You have two asynchronous processes, both of which can result in having your NSURLConnectionDataDelegate methods in self being called. But if they happen at the same time, they're going to step on top of each other (you're presumably using a single NSMutableData variable to reference the data being downloaded).
Either create dedicated class that you can instantiate once for each of your two NSURLConnection requests (a NSOperation based approach, like AFNetworking, is ideal), or use sendAsynchronousRequest instead. But don't use a single object as the delegate for two concurrent NSURLConnection requests at the same time.
If you wanted to see a minimalist download operation, it might look like:
// NetworkOperation.h
#import <Foundation/Foundation.h>
typedef void(^DownloadCompletion)(NSData *data, NSError *error);
#interface NetworkOperation : NSOperation
- (id)initWithURL:(NSURL *)url completion:(DownloadCompletion)completionBlock;
#property (nonatomic, copy) NSURL *url;
#property (nonatomic, copy) DownloadCompletion downloadCompletionBlock;
#end
and
// NetworkOperation.m
#import "NetworkOperation.h"
#interface NetworkOperation () <NSURLConnectionDataDelegate>
#property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
#property (nonatomic, readwrite, getter = isFinished) BOOL finished;
#property (nonatomic, strong) NSMutableData *data;
#end
#implementation NetworkOperation
#synthesize finished = _finished;
#synthesize executing = _executing;
- (id)initWithURL:(NSURL *)url completion:(DownloadCompletion)downloadCompletionBlock
{
self = [super init];
if (self) {
self.url = url;
self.downloadCompletionBlock = downloadCompletionBlock;
_executing = NO;
_finished = NO;
}
return self;
}
#pragma mark - NSOperation related stuff
- (void)start
{
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
NSURLRequest *request = [NSURLRequest requestWithURL:self.url];
NSAssert(request, #"%s: requestWithURL failed for URL '%#'", __FUNCTION__, [self.url absoluteString]);
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:#"isExecuting"];
_executing = executing;
[self didChangeValueForKey:#"isExecuting"];
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:#"isFinished"];
_finished = finished;
[self didChangeValueForKey:#"isFinished"];
}
- (BOOL)isConcurrent
{
return YES;
}
#pragma mark NSURLConnectionDataDelegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
self.data = [NSMutableData data];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if ([self isCancelled]) {
[connection cancel];
self.executing = NO;
self.finished = YES;
return;
}
[self.data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (self.downloadCompletionBlock) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.downloadCompletionBlock(self.data, nil);
self.downloadCompletionBlock = nil;
}];
}
self.executing = NO;
self.finished = YES;
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if (self.downloadCompletionBlock) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.downloadCompletionBlock(nil, error);
self.downloadCompletionBlock = nil;
}];
}
self.executing = NO;
self.finished = YES;
}
#end
And then, when you wanted to use it, it might look like:
NSOperationQueue *networkQueue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;
// ----- Gets my own profile picture, using requestForMe -----
FBRequest *request = [FBRequest requestForMe];
[request startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
//handle response
if(!error) {
//Some methods not included for breveity, includes facebookID used below
NSURL *pictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookID]];
[networkQueue addOperation:[[NetworkOperation alloc] requestWithURL:pictureURL completion:^(NSData *data, NSError *error) {
if (!error) {
self.meImageView.image = [UIImage imageWithData:data];
}
}]];
}
}];
// ----- Gets a profile picture of my friend, using requestForMyFriends -----
FBRequest *requestForFriends = [FBRequest requestForMyFriends];
[requestForFriends startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error){
//Other methods not included, including facebookFriendID
NSURL *friendPictureURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=large&return_ssl_resources=1", facebookFriendID]];
[networkQueue addOperation:[[NetworkOperation alloc] requestWithURL:friendPictureURL completion:^(NSData *data, NSError *error) {
if (!error) {
self.friendImageView.image = [UIImage imageWithData:data];
}
}]];
}
}];

Appending data from downloaded from the web in NSMutableData

I'm trying to append data from a CSV file that I'm getting form the internet. Something is going wrong and I can't figure out what. My log is not returning anything for "Field" or "Value". I'm holding the data as NSMutableData and trying to append it. Am I overwriting it in any way? Something in the data storage/retrieval process must be going wrong... Could you pls take a look at this code and tell me if I'm doing something wrong:
#import "ViewController.h"
#import "CHCSVParser.h"
#interface ViewController ()
#end
#implementation ViewController
#synthesize parser = _parser;
bool isStatus;
#synthesize apendData;
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"connectionDidFinishLoading");
NSString *myString;
myString = [[NSString alloc] initWithData: apendData encoding:NSUTF8StringEncoding];
_parser = [[CHCSVParser alloc] initWithContentsOfCSVFile:[NSHomeDirectory() stringByAppendingPathComponent:myString]];
_parser.delegate = self;
[_parser parse];
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"data: %#", data);
if (apendData)
[apendData appendData:data];
else
apendData = [[NSMutableData alloc] initWithData:data];
}
- (void)loadDatafromURL
{
NSURL *url = [NSURL URLWithString:#"http://www.google.com/trends/trendsReport?hl=en-US&cat=0-13&date=today%207-d&cmpt=q&content=1&export=1"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"%#", [error description]);
}
- (void)parser:(CHCSVParser *)parser didBeginLine:(NSUInteger)recordNumber
{
if (recordNumber == 41) {
isStatus = YES;
NSLog(#"ParsingStart..."); // Not getting this
}
}
- (void)parser:(CHCSVParser *)parser didEndLine:(NSUInteger)recordNumber
{
if (recordNumber == 50) {
NSLog(#"ParsingEnd..."); // Not getting this
[parser cancelParsing];
}
}
- (void)parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex
{
if ((isStatus = YES)) {
NSLog(#"YES is working");
if (fieldIndex == 0) {
NSLog(#"Field = %#", field); // Just getting "Field = "
}
if (fieldIndex == 1){
NSLog(#"Value = %#", field); // Not getting this
}
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self loadDatafromURL];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end

NSArray with multiple nested requests

I have an app that uses a segmentedControl. First item is an "All" item, where the rest is created from an array based on result from webservice. When "All" is selected I want to request all the request.
How can I go about this,
NSArray *urls = [NSArray arrayWithObjects:#"http://service/group/1/",
#"http://service/group/2/", nil];
I want to collect all result from the calls into a collection and display it in a UITableView when the "All" item is selected and probably in viewDidLoad.
For the other segments only one of the request is issued and callback with an array that then is used in:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
I have tried to look at this example for making the request from the array MultipleDownloads
Thanks,
The method in my viewController to initiate the multiple download:
- (void)requestChildrenInBackground {
queue = [[NSOperationQueue alloc] init];
//Todo remove hard coded and get from previous request respons
NSArray *urls = [NSArray arrayWithObjects: #"http://service/1/children",
#"http://service/2/children",
#"http://service/3/children", nil];
NSLog(#"%#", urls);
for (NSString * url in urls)
{
GetSchedule *operation =
[GetSchedule urlDownloaderWithUrlString:url];
[queue addOperation:operation];
}
}
This is how the multiple request gets handled:
#import "GetSchedule.h"
#import "JSON.h"
#import "Authentication.h"
#import "AttendanceReportViewController.h"
#interface GetSchedule ()
- (void)finish;
#end
#implementation GetSchedule
#synthesize appDelegate;
#synthesize username;
#synthesize password;
#synthesize authenticationString;
#synthesize encodedLoginData;
#synthesize schedulesArray;
#synthesize url = _url;
#synthesize statusCode = _statusCode;
#synthesize data = _data;
#synthesize error = _error;
#synthesize isExecuting = _isExecuting;
#synthesize isFinished = _isFinished;
+ (id)urlDownloaderWithUrlString:(NSString *)urlString {
NSURL * url = [NSURL URLWithString:urlString];
GetSchedule *operation = [[self alloc] initWithUrl:url];
return [operation autorelease];
}
- (id)initWithUrl:(NSURL *)url {
self = [super init];
if (self == nil)
return nil;
_url = [url copy];
_isExecuting = NO;
_isFinished = NO;
return self;
}
- (void)dealloc
{
[username release];
[password release];
[encodedLoginData release];
[_url release];
[_connection release];
[_data release];
[_error release];
[super dealloc];
}
- (BOOL)isConcurrent
{
return YES;
}
- (void)start
{
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:#selector(start) withObject:nil waitUntilDone:NO];
return;
}
self.username = appDelegate.username;
self.password = appDelegate.password;
Authentication *auth = [[Authentication alloc] init];
authenticationString = (NSMutableString*)[#"" stringByAppendingFormat:#"%#:%#", username, password];
self.encodedLoginData = [auth encodedAuthentication:authenticationString];
[auth release];
NSLog(#"operation for <%#> started.", _url);
[self willChangeValueForKey:#"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:#"isExecuting"];
// Setup up the request with the url
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]
initWithURL:_url];
[request setHTTPMethod:#"GET"];
[request setValue:[NSString stringWithFormat:#"Basic %#", encodedLoginData] forHTTPHeaderField:#"Authorization"];
_connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
if (_connection == nil)
[self finish];
else {
_data = [[NSMutableData alloc] init];
}
}
- (void)finish
{
NSLog(#"operation for <%#> finished. "
#"status code: %d, error: %#, data size: %u",
_url, _statusCode, _error, [_data length]);
[_connection release];
_connection = nil;
[self willChangeValueForKey:#"isExecuting"];
[self willChangeValueForKey:#"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:#"isExecuting"];
[self didChangeValueForKey:#"isFinished"];
}
#pragma mark -
#pragma mark NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
//[_data release];
//_data = [[NSMutableData alloc] init];
[_data setLength:0];
NSHTTPURLResponse * httpResponse = (NSHTTPURLResponse *)response;
_statusCode = [httpResponse statusCode];
}
- (void)connection:(NSURLConnection *)connection
didReceiveData:(NSData *)data
{
[_data appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Parse the responseData of json objects retrieved from the service
SBJSON *parser = [[SBJSON alloc] init];
NSString *jsonString = [[NSString alloc] initWithData:_data encoding:NSUTF8StringEncoding];
NSDictionary *jsonData = [parser objectWithString:jsonString error:nil];
NSMutableArray *array = [jsonData objectForKey:#"Children"];
schedulesArray = [NSMutableArray array];
[schedulesArray addObject:array];
// Callback to AttendanceReportViewController that the responseData finished loading
[attendanceReportViewController loadSchedule];
[self finish];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
_error = [error copy];
[self finish];
}
#end
When all data is received I want to call back to my ViewController and get an array with all data from all request made.
Create a downloader
Create a downloader class using NSURLConnection.
Keep a member variable called downloaderObjectId.
Write a delegate method for this object. This method will pass the downloaded data and downloaderObjectId back to the delegate.
In the delegate.
Create multiple downloader objects(As per your ncessity) with unique value for the downloaderObjectId.
Store these objects in a NSMutableDictionary
Key for each object will be downloaderObjectId. so that when the delegate method is called after download you take the exact object back from the NSMutableDictionary using this key.
Main point.
Each time delegate is called. You should remove the object from dictionary(The object who is done with the download and called his delgate. You can identify this object by the key downloaderObjectId he holds. )
Then check the count of dictionary. If it is zero you can make sure that your downloads are completed. So you can call your viewcontroller.

How to use the JSON Parsing?

I am a fresher. I don't know how to use JSON for Parsing .I am really very confuse because the tutorial i have read are out dated because apple has deprecated all the API's and documents .Thats what i found .So anyone can give me the start to play with JSON?
You can use SBJSON
You can find code at GITHUB
Here's some links on "Picking the best JSON library":
Comparison of JSON Parser for Objective-C (JSON Framework, YAJL, TouchJSON, etc)
Best JSON library to use when developing an iPhone application?
And now for the shameless plug for my own JSON parsing solution, JSONKit:
What's the most efficient way of converting a 10 MB JSON response into an NSDictionary?
Cocoa JSON parsing libraries, part 2
You need to use third-party code to parse JSON. Three popular libraries/frameworks are:
SBJSON, aka json-framework
TouchJSON
yajl-objc
Choose one of them, download its source code and add it do your project.
You can use your custom framwork for it just as below
#import <Foundation/Foundation.h>
#import "SVProgressHUD.h"
#protocol JsonDataDelegate <NSObject>
#required
- (void) JsonprocessSuccessful: (NSString *)receivedData;
-(void) JsonprocessFail;
#end
#interface JsonRest : NSObject
{
id <JsonDataDelegate> delegate;
NSURLConnection *theConnection;
NSMutableData *webData;
BOOL HadGotResponse;
BOOL HasFailedConnection;
UIAlertView *alert;
}
#property (retain) id delegate;
#property(nonatomic,retain) NSURLConnection *theConnection;
#property (nonatomic)BOOL HadGotResponse;
#property (nonatomic)BOOL HasFailedConnection;
-(void)GetServiceCall:(NSString *)url Paraqmeter:(NSString*)parameter;
-(void)Set2Defaults;
#end
#import "JsonRest.h"
#define BASE_URL #"http://www.abc.com"
#define DOMAIN #"GETLIST"
#implementation JsonRest
#synthesize delegate,HadGotResponse,HasFailedConnection,theConnection;
- (id)init
{
if (self)
{
// Initialization code.
//appdel = (ReachCastAppDelegate*)[[UIApplication sharedApplication]delegate];
}
return self;
}
-(void)GetServiceCall:(NSString *)url Paraqmeter:(NSString*)parameter
{
[SVProgressHUD startActivity : #"Loading..."];
NSString *strUrl=[NSString stringWithFormat:#"%#%#?%#",BASE_URL,url,parameter];
NSURL *url1=[NSURL URLWithString:strUrl];
NSLog(#"domainURL :: %#",strUrl);
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url1];
[req setTimeoutInterval:60.0];
[req setHTTPMethod:#"GET"];
// if([HttpMethod isEqualToString:#"GET"])
// {
// [req setHTTPMethod:HttpMethod];
//
// }
// else if([HttpMethod isEqualToString:#"PUT"])
// {
// NSData *requestData = [NSData dataWithBytes: [BodyString UTF8String] length: [BodyString length]];
//
// [req setValue:valueforput forHTTPHeaderField:#"Content-type"];
// [req setHTTPMethod:HttpMethod];
// [req setHTTPBody: requestData];
//
// }
// else if ([HttpMethod isEqualToString:#"POST"]){
//
// NSData *requestData = [NSData dataWithBytes: [BodyString UTF8String] length: [BodyString length]];
//
// [req setValue:valueforput forHTTPHeaderField:#"Content-type"];
// [req setHTTPMethod:HttpMethod];
// [req setHTTPBody: requestData];
//
// }
if(theConnection)
[self Set2Defaults];
theConnection=[[NSURLConnection alloc]initWithRequest:req delegate:self];
if(theConnection)
webData=[[NSMutableData data]retain];
else
NSLog(#"Connection Failed !!!");
NSLog(#"Has got response");
}
-(void)Set2Defaults {
if (webData != nil){
[webData release];
webData = nil;
}
if (theConnection != nil) {
[theConnection release];
if (theConnection != nil)
theConnection = nil;
}
}
#pragma mark -
#pragma mark NSURL Connection Delegate methods
-(void) connection:(NSURLConnection *) connection didReceiveResponse:(NSURLResponse *) respons{
[webData setLength: 0];
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)respons;
int statusCode = [httpResponse statusCode];
NSLog(#"statusCode ---- >> %d",statusCode);
if (200 != statusCode) {
// if (401 == statusCode) {
}
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data {
[webData appendData:data];
}
-(void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error {
[SVProgressHUD stopActivity];
[self Set2Defaults];
self.HadGotResponse=NO;
self.HasFailedConnection=YES;
// txtWindSpeed.text = #"";
[[self delegate] JsonprocessFail];
NSLog(#"Connection Failed!!!");
//[self HideLoadingDialogue];
alert = [[[UIAlertView alloc] initWithTitle:#"ERROR" message:#"Internet Connection Failed.Please try again." delegate:self cancelButtonTitle:nil otherButtonTitles:nil] autorelease];
//alert.frame=CGRectMake(alert.frame.origin.x, alert.frame.origin.y, alert.frame.size.width, 130);
NSLog(#"%F",alert.frame.origin.y);
[alert show];
alert.frame=CGRectMake(alert.frame.origin.x, alert.frame.origin.y+25, alert.frame.size.width, 130);
//[noConnect release];
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:#selector(timerCall) userInfo:nil repeats:NO];
}
-(void)timerCall
{
[alert dismissWithClickedButtonIndex:0 animated:TRUE];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[SVProgressHUD stopActivity];
self.HasFailedConnection=NO;
NSString *theXML;
theXML = [[NSString alloc] initWithBytes: [webData mutableBytes] length:[webData length] encoding:NSASCIIStringEncoding];
//NSLog(#"theXML Values : %#", theXML);
//self.ResponseDict = [theXML JSONValue];
[[self delegate] JsonprocessSuccessful:theXML];
[theXML release];
}
-(void)dealloc
{
if (webData != nil){
[webData release];
webData = nil;
}
if (theConnection != nil) {
[theConnection release];
if (theConnection != nil)
theConnection = nil;
}
[super dealloc];
// [ResponseDict release];
}
#end
You can use this using below
-(void)getJsonCalling
{
#try {
NSString *strParameter=[NSString stringWithFormat:#"param1=%#&param2=%#",param1,param2];
objJsonRest=[[JsonRest alloc]init];
[objJsonRest setDelegate:self];
[objJsonRest GetServiceCall:DOMAIN Paraqmeter:strParameter];
[objJsonRest release];
}
#catch (NSException *exception) {
NSLog(#"Exception in %s %#",__FUNCTION__,exception);
}
}
- (void) JsonprocessSuccessful: (NSString *)receivedData
{
}
-(void) JsonprocessFail;
{
NSLog(#"FAIL");
}

Downloading a Large File - iPhone SDK

I am using Erica Sadun's method of Asynchronous Downloads (link here for the project file: download), however her method does not work with files that have a big size (50 mb or above). If I try to download a file above 50 mb, it will usually crash due to a memory crash. Is there anyway I can tweak this code so that it works with large files as well? Here is the code I have in the DownloadHelper Classes (which is already in the download link):
.h
#protocol DownloadHelperDelegate <NSObject>
#optional
- (void) didReceiveData: (NSData *) theData;
- (void) didReceiveFilename: (NSString *) aName;
- (void) dataDownloadFailed: (NSString *) reason;
- (void) dataDownloadAtPercent: (NSNumber *) aPercent;
#end
#interface DownloadHelper : NSObject
{
NSURLResponse *response;
NSMutableData *data;
NSString *urlString;
NSURLConnection *urlconnection;
id <DownloadHelperDelegate> delegate;
BOOL isDownloading;
}
#property (retain) NSURLResponse *response;
#property (retain) NSURLConnection *urlconnection;
#property (retain) NSMutableData *data;
#property (retain) NSString *urlString;
#property (retain) id delegate;
#property (assign) BOOL isDownloading;
+ (DownloadHelper *) sharedInstance;
+ (void) download:(NSString *) aURLString;
+ (void) cancel;
#end
.m
#define DELEGATE_CALLBACK(X, Y) if (sharedInstance.delegate && [sharedInstance.delegate respondsToSelector:#selector(X)]) [sharedInstance.delegate performSelector:#selector(X) withObject:Y];
#define NUMBER(X) [NSNumber numberWithFloat:X]
static DownloadHelper *sharedInstance = nil;
#implementation DownloadHelper
#synthesize response;
#synthesize data;
#synthesize delegate;
#synthesize urlString;
#synthesize urlconnection;
#synthesize isDownloading;
- (void) start
{
self.isDownloading = NO;
NSURL *url = [NSURL URLWithString:self.urlString];
if (!url)
{
NSString *reason = [NSString stringWithFormat:#"Could not create URL from string %#", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
return;
}
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
if (!theRequest)
{
NSString *reason = [NSString stringWithFormat:#"Could not create URL request from string %#", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
return;
}
self.urlconnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!self.urlconnection)
{
NSString *reason = [NSString stringWithFormat:#"URL connection failed for string %#", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
return;
}
self.isDownloading = YES;
// Create the new data object
self.data = [NSMutableData data];
self.response = nil;
[self.urlconnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void) cleanup
{
self.data = nil;
self.response = nil;
self.urlconnection = nil;
self.urlString = nil;
self.isDownloading = NO;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)aResponse
{
// store the response information
self.response = aResponse;
// Check for bad connection
if ([aResponse expectedContentLength] < 0)
{
NSString *reason = [NSString stringWithFormat:#"Invalid URL [%#]", self.urlString];
DELEGATE_CALLBACK(dataDownloadFailed:, reason);
[connection cancel];
[self cleanup];
return;
}
if ([aResponse suggestedFilename])
DELEGATE_CALLBACK(didReceiveFilename:, [aResponse suggestedFilename]);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
// append the new data and update the delegate
[self.data appendData:theData];
if (self.response)
{
float expectedLength = [self.response expectedContentLength];
float currentLength = self.data.length;
float percent = currentLength / expectedLength;
DELEGATE_CALLBACK(dataDownloadAtPercent:, NUMBER(percent));
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// finished downloading the data, cleaning up
self.response = nil;
// Delegate is responsible for releasing data
if (self.delegate)
{
NSData *theData = [self.data retain];
DELEGATE_CALLBACK(didReceiveData:, theData);
}
[self.urlconnection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self cleanup];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.isDownloading = NO;
NSLog(#"Error: Failed connection, %#", [error localizedDescription]);
DELEGATE_CALLBACK(dataDownloadFailed:, #"Failed Connection");
[self cleanup];
}
+ (DownloadHelper *) sharedInstance
{
if(!sharedInstance) sharedInstance = [[self alloc] init];
return sharedInstance;
}
+ (void) download:(NSString *) aURLString
{
if (sharedInstance.isDownloading)
{
NSLog(#"Error: Cannot start new download until current download finishes");
DELEGATE_CALLBACK(dataDownloadFailed:, #"");
return;
}
sharedInstance.urlString = aURLString;
[sharedInstance start];
}
+ (void) cancel
{
if (sharedInstance.isDownloading) [sharedInstance.urlconnection cancel];
}
#end
And finally this is how I write the file with the two classes above it:
- (void) didReceiveData: (NSData *) theData
{
if (![theData writeToFile:self.savePath atomically:YES])
[self doLog:#"Error writing data to file"];
[theData release];
}
If someone could help me out I would be so glad!
Thanks,
Kevin
Replace the in-memory NSData *data with an NSOutputStream *stream. In -start create the stream to append and open it:
stream = [[NSOutputStream alloc] initToFileAtPath:path append:YES];
[stream open];
As data comes in, write it to the stream:
NSUInteger left = [theData length];
NSUInteger nwr = 0;
do {
nwr = [stream write:[theData bytes] maxLength:left];
if (-1 == nwr) break;
left -= nwr;
} while (left > 0);
if (left) {
NSLog(#"stream error: %#", [stream streamError]);
}
When you're done, close the stream:
[stream close];
A better approach would be to add the stream in addition to the data ivar, set the helper as the stream's delegate, buffer incoming data in the data ivar, then dump the data ivar's contents to the helper whenever the stream sends the helper its space-available event and clear it out of the data ivar.
I have a slight modification to the above code.
Use this function, it works fine for me.
- (void) didReceiveData: (NSData*) theData
{
NSOutputStream *stream=[[NSOutputStream alloc] initToFileAtPath:self.savePath append:YES];
[stream open];
percentage.hidden=YES;
NSString *str=(NSString *)theData;
NSUInteger left = [str length];
NSUInteger nwr = 0;
do {
nwr = [stream write:[theData bytes] maxLength:left];
if (-1 == nwr) break;
left -= nwr;
} while (left > 0);
if (left) {
NSLog(#"stream error: %#", [stream streamError]);
}
[stream close];
}
Try AFNetworking. And:
NSString *yourFileURL=#"http://yourFileURL.zip";
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:yourFileURL]];
AFURLConnectionOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *cacheDir = [NSSearchPathForDirectoriesInDomains
(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [cacheDir stringByAppendingPathComponent:
#"youFile.zip"];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
//show here your downloading progress if needed
}];
[operation setCompletionBlock:^{
NSLog(#"File successfully downloaded");
}];
[operation start];