retaining when returning? - iphone

Should I be retaining the responseData that I am returning
// METHOD
-(NSData *)dataFromTurbine:(NSString *)pathToURL {
NSURL *url = [[NSURL alloc] initWithString:pathToURL];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
NSHTTPURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
[request release];
[url release];
return responseData;
}
.
// CALLED
NSData *newData = dataFromTurbine(kTurbineDataPath);
[doSomething newData];

Since the method name doesn't start with init, new or copy, dataFromTurbine should return an autoreleased instance of NSData. (Which is already true now for responseData)
The calling method then has ownership, and should retain if needed.

In a word, no.
The NSData object you get from NSURLConnection is autoreleased, so you should retain/release it only if you need to keep it. Otherwise, it will be automatically released for you at the next pass of the run loop.

Related

json return to the format

Api Address:
http://suggest.taobao.com/sug?area=etao&code=utf-8&callback=KISSY.Suggest.callback&q=iphone
return:
KISSY.Suggest.callback({"result": [["iphone4s", "9809"], ["iphone5", "13312"], ["iphone4 手机", "69494400"], ["iphone5 港行", "14267"], ["iphone5三网", "2271160"], ["iphone4手机壳", "6199679"], ["iphone 5手机壳", "2527284"], ["iphone 5 保护壳", "5727586"], ["iphone 4贴膜", "147271"], ["iphone5壳", "2628540"]]})
NSURL * url = [NSURL URLWithString:#"http://suggest.taobao.com/sug?area=etao&code=utf-8&callback=KISSY.Suggest.callback&q=iphone"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSHTTPURLResponse* urlResponse = nil;
NSError * error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
NSData *date = [NSData alloc]init
SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
// NSMutableArray *array=[NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
NSMutableArray *array = [jsonParser objectWithData:responseData];
NSLog(#"%#",array);
this array is null. i dont know the reason.
as i refer you request URL ,it has callback in it, if you keep it, it will not return you json as response, so remove "&callback=KISSY.Suggest.callback" from your URL
// Make sure you have include SBJSON files in your Project, as well you have imported header in your View Controller
#import "JSON.h"
// your request URL
NSURL * url = [NSURL URLWithString:#"http://suggest.taobao.com/sug?area=etao&code=utf-8&q=iphone"];
// URL Request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
NSHTTPURLResponse* urlResponse = nil;
NSError * error = nil;
// initiate Request to get Data
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&error];
// Encode your Response
NSString *content = [[NSString alloc] initWithBytes:[responseData bytes] length:[responseData length] encoding:NSUTF8StringEncoding];
// Now read a Dictionary from it using SBJSON Parser
NSDictionary *responseDict = [content JSONValue];
NSLog(#"Response [%#]",responseDict);
I'm not familiar with the SBJsonParser, but the format of the returned string looks like JSONP, not JSON. I would imagine simply cleaning out the wrapper call would get you what you are after.
Also, note that the 'root' of your response is a dictionary, not an array.
{"result": [[...
means that the code might should look like this:
NSDictionary *response = //... decode
NSArray *results = [response objectForKey:#"result"];
Edited
You just need to use http://suggest.taobao.com/sug?area=etao&code=utf-8&q=iphone instead of http://suggest.taobao.com/sug?area=etao&code=utf-8&callback=KISSY.Suggest.callback&q=iphone you own code will work..

iPhone EXC_BAD_ACCESS on accessing instance var in block

I've got a question about instance variables in combination with blocks & arc in Objective C with IOS5.
Shortly, when i access this code, the iPhone gives me an EXC_BAD_ACCESS and terminates:
- (void) doRequest: (void (^)(XMLTreeNode*) )completionHandler {
NSString * urlString = [NSString stringWithFormat:#"blablaurl=%#&", action];
for( NSString* key in parameters ){
urlString = [urlString stringByAppendingFormat:#"&%#=%#", key, [parameters objectForKey:key]];
}
NSURL * url = [NSURL URLWithString:urlString];
NSLog( #"Visiting: %#", [url absoluteString] );
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"GET"];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * response, NSData * data, NSError * err) {
NSLog( #"Params=%#", parameters );
completionHandler(e);
}];
}
The exc_bad_access occurs on:
NSLog( #"Params=%#", parameters );
(parameters is an instance variable of the class).. Just defined in the header file, no special property or what-so-ever..
Why does it crash and how can i prevent it? Thanks!
My guess is that it crashes because the objects lifetime is over after the doRequest call, and thus ARC cleans up all variables (and with that the parameter var).. When the urlconnection completes and calls the block, the instance variables are aready cleaned up..
parameters is clean up by ARC.
2 case here:
Your main object isn't released before the block completion: Just create an strong,nonatomic property for "parameters". Using the "strong" keyword in your property say to ARC that you need "parameters" during all your main object life
Your main object is released before the block completion: create a new __block pointer to your object
__block blockParameters = parameters;
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * response, NSData * data, NSError * err) {
NSLog( #"Params=%#", blockParameters );
completionHandler(e);
}];
Using the "__block" keyword say to ARC that you need "blockParameters" during all your block life
You have only the parameters of the block at your disposal, i.e. response, data and error in this case. You could use [response URL] to get at the parameters.
NSString *path = [[response URL] path];
NSString *secondPartOfURL = [[path componentsSeparatedByString:#"?"] objectAtIndex:1];
NSArray *keyValuePairs = [secondPartOfURL componentsSeparatedByString#´:#"&"];
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
for (NSString *pair in keyValuePairs) {
NSArray *keyValue = [pair componentsSeparatedByString:#"="];
[parameters setValue:[keyValue objectAtIndex:1]
forKey:[keyValue objectAtIndex:0]];
}
NSLog(#"Params=%#", parameters);

Why did the release statement here crashes the app?

NSError *theError = nil;
NSArray *keys = [NSArray arrayWithObjects:#"password", #"userId", nil];
NSArray *objects = [NSArray arrayWithObjects:passwordTextField.text, userNameTextField.text, nil];
NSDictionary *requestDictionary = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
NSString *JSONString =[requestDictionary JSONRepresentation];
NSData *JSONData =[JSONString dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"JSONString :%#", JSONString);
NSLog(#"JSONData :%#", JSONData);
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://153.20.32.74/11AprP306/passenger/jsonitem"]];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:JSONData];
[request setHTTPMethod:#"POST"];
NSURLResponse *theResponse =[[NSURLResponse alloc]init];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&theError];
NSLog(#"response : %#", theResponse);
NSLog(#"error : %#", theError);
NSLog(#"data : %#", data);
NSMutableString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"string: %#", string);
[string release];
//[theResponse release]; // this statement crashes the app
Has it got something with to do with this statement :NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&theError];
I see a & symbol used. What does it means?
I'll post this as a new answer as you edited your question.
You are doing it the wrong way.
It is the responsability of sendSynchronousRequest:returningResponse:error: to create the response for you (or the error if something went wrong)
This is why you need to pass a pointer to theResponse and a pointer to theError
When the call to sendSynchronousRequest:returningResponse:error: is done, theResponse will be created and most importantly autoreleased (by sendSynchronousRequest:returningResponse:error) !!
So in the end you are back to the autorelease/over release issue.
The correct code is:
NSURLResponse *theResponse = nil; // no need to init it will be done later on
NSError *theError = nil; // no need to init either
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&theResponse
error:&theError];
if (aError != nil) { } // handle error
else {} // handle your response data
//no need to release theResponse
Well, you autorelease theResponse when you instantiate it, so releasing it twice is causing your problem. Either don't make the autorelease call or don't make the release call.
Personally, I'd get rid of the autorelease. release gives finer-grained control over the run of your program.
Oh, and the & there is nothing to worry about -- it just passes the address of the variable it proceeds. In this case, you need to pas an NSURLResponse**. Since you have an NSURLResponse*, you pass a reference to it.
This is because theResponse has sent the message autorelease in:
NSURLResponse *theResponse =[[[NSURLResponse alloc]init] autorelease];
If you release an object that has been autoreleased you will cause your application to crash for the Garbage Collector will over release the object.
The & simply means "give me the address of theError and theResponse (basically you are passing a pointer of pointer which is required by the method sendSynchronousRequest:returningResponse:error:)
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request
returningResponse:(NSURLResponse **)response
error:(NSError **)error
The NSURLResponse ** and NSError ** means 'address of address' so give them only theError or theResponse (without the &) would simply give the method 'their address' when it is expecting something else.

When release methods local variables in Obj-C

I'm developing an iPhone application and I've just created this method (it's in a singleton class):
- (NSDictionary *)getLastPosts
{
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:http://example.org/last/]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary *data_dict = [parser objectWithString:json_string error:nil];
// release stuff
[parser release];
[request release];
[response release];
[json_string release];
return data_dict;
}
I'm a newbie obj-c developer so I'm not sure of this two things:
Is it correct the four vars release in the method's end?
When should I release the NSDictionary data_dict?
UPDATE 1
If data_dict was NSDictionary *data_dict = [[NSDictionary alloc] init] when I'll should release it?
UPDATE 2
In the caller I have this:
- (void)callerMethod
{
NSDictionary *tmpDict = [mySingleton getLastPosts];
NSLog(#"retain count: %d", [tmpDict retainCount]);
}
and the debug console prints:
retain count: 2
Why "Xcode Analyze" says me these lines?
And why the retain count it's 2?
In general, it is good to release objects you do not need any more.
But remember
- Only things that have alloc, new or copy in their initialization need to be released. Otherwise they are already autoreleased.
So, it is ok to release the parser, not ok to release the request, not ok to release the response, ok to release the json_string.
SBJsonParser *parser = [[SBJsonParser alloc] init];
You called init, then you own the instance and you need to release it.
NSURLRequest *request = [NSURLRequest requestWithURL:
[NSURL URLWithString:http://example.org/last/]];
You called a class method that returns an autoreleased instance which will be added to the autorelease poll.
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
Autoreleased.
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
You called init, you will need to release it.
NSDictionary *data_dict = [parser objectWithString:json_string error:nil];
Returned instance, autoreleased.
Thus you just need to release two of them:
[parser release];
[json_string release];
if NSDictionary *data_dict = [[NSDictionary alloc] init] then you would need to autorelease it yourself: the convention is that any instance returned by a method is autoreleased.
By the way by autoreleasing it you make sure that it will be available until the autorelease pool is emptied (unless you call release on it).
To autorelease it:
return [data_dict autorelease];
It is correct to release parser and json_string because these are created with methods containing "alloc". It is incorrect to release the others because they are autoreleased.
You never have to release data_dict in this method, since it is autoreleased.
Please read the Objective-C memory management rules.

Encoding Problem in iphone

Below is my code..
NSString *strResponce = [[NSString alloc] initWithData:JsonData encoding:NSASCIIStringEncoding];
here string has some data.
[JsonData release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
ArrayWebContent=[json objectWithString:strResponce error:&error];
But array is null.
any suggestion....
check your json data first put the content of the string strResponce in to the url
Checking json data are proper for parsing
if it gonna generate the parse error than you should check the content of the ws as it may content special charactor for which iphone can not support parsing
good luck
Try with below functions.
- (id) objectWithUrl:(NSURL *)url
{
SBJSON *jsonParser = [SBJSON new];
NSString *jsonString = [self stringWithUrl:url];
// Parse the JSON into an Object
return [jsonParser objectWithString:jsonString error:NULL];
}
- (NSString *)stringWithUrl:(NSURL *)url
{
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestReturnCacheDataElseLoad
timeoutInterval:30];
// Fetch the JSON response
NSData *urlData;
NSURLResponse *response;
NSError *error;
// Make synchronous request
urlData = [NSURLConnection sendSynchronousRequest:urlRequest
returningResponse:&response
error:&error];
// Construct a String around the Data from the response
return [[NSString alloc] initWithData:urlData encoding:NSUTF8StringEncoding];
}
Let me know for any difficulty.