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);
Related
I have a EMR app and i want that i may send the data which i have collected like images and voice to server. in data base so how can i do this . Is there any way to send these data to server through post method.
Here is an example of a HTTP Post request
// define your form fields here:
NSString *content = #"field1=42&field2=Hello";
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.example.com/form.php"]];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[content dataUsingEncoding:NSISOLatin1StringEncoding]];
// generates an autoreleased NSURLConnection
[NSURLConnection connectionWithRequest:request delegate:self];
Might want to reference http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSURLConnection_Class/Reference/Reference.html
This tutorial is also helpful http://www.raywenderlich.com/2965/how-to-write-an-ios-app-that-uses-a-web-service
In that case, you can do follow two ways:
1. if you strictly like to using POST (i like), u can using cocoahttpserver project:
https://github.com/robbiehanson/CocoaHTTPServer
In iphone app, you can do this code to send POST request:
-(NSDictionary *) getJSONAnswerForFunctionVersionTwo:(NSString *)function
withJSONRequest:(NSMutableDictionary *)request;
{
[self updateUIwithMessage:#"server download is started" withObjectID:nil withLatestMessage:NO error:NO];
NSDictionary *finalResultAlloc = [[NSMutableDictionary alloc] init];
#autoreleasepool {
NSError *error = nil;
NSString *jsonStringForReturn = [request JSONStringWithOptions:JKSerializeOptionNone serializeUnsupportedClassesUsingBlock:nil error:&error];
if (error) NSLog(#"CLIENT CONTROLLER: json decoding error:%# in function:%#",[error localizedDescription],function);
NSData *bodyData = [jsonStringForReturn dataUsingEncoding:NSUTF8StringEncoding];
NSData *dataForBody = [[[NSData alloc] initWithData:bodyData] autorelease];
//NSLog(#"CLIENT CONTROLLER: string lenght is:%# bytes",[NSNumber numberWithUnsignedInteger:[dataForBody length]]);
NSString *functionString = [NSString stringWithFormat:#"/%#",function];
NSURL *urlForRequest = [NSURL URLWithString:functionString relativeToURL:mainServer];
NSMutableURLRequest *requestToServer = [NSMutableURLRequest requestWithURL:urlForRequest];
[requestToServer setHTTPMethod:#"POST"];
[requestToServer setHTTPBody:dataForBody];
[requestToServer setTimeoutInterval:600];
[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[urlForRequest host]];
NSData *receivedResult = [NSURLConnection sendSynchronousRequest:requestToServer returningResponse:nil error:&error];
if (error) {
NSLog(#"CLIENT CONTROLLER: getJSON answer error download:%#",[error localizedDescription]);
[self updateUIwithMessage:[error localizedDescription] withObjectID:nil withLatestMessage:YES error:NO];
[finalResultAlloc release];
return nil;
}
NSString *answer = [[NSString alloc] initWithData:receivedResult encoding:NSUTF8StringEncoding];
JSONDecoder *jkitDecoder = [JSONDecoder decoder];
NSDictionary *finalResult = [jkitDecoder objectWithUTF8String:(const unsigned char *)[answer UTF8String] length:[answer length] error:&error];
[finalResultAlloc setValuesForKeysWithDictionary:finalResult];
[answer release];
[self updateUIwithMessage:#"server download is finished" withObjectID:nil withLatestMessage:NO error:NO];
if (error) NSLog(#"CLIENT CONTROLLER: getJSON answer failed to decode answer with error:%#",[error localizedDescription]);
}
NSDictionary *finalResultToReturn = [NSDictionary dictionaryWithDictionary:finalResultAlloc];
[finalResultAlloc release];
return finalResultToReturn;
}
Don't forget to pack attributes with images to base64.
Finally, if u don't like to keep data, which u send in you mac app, u can send to u database using any database C api. I recommend to using core data to save receive data.
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.
I have an iPhone app which sends a request to a url posting a variable called submit:
+(NSMutableArray*)getQuestions:(NSString*)section from: (NSString*) url{
NSMutableArray *questions = [[NSMutableArray alloc] init];
//connect to database given by url
//NSError *error = nil;
//NSURLResponse *response = nil;
NSMutableString* myRequestString = [[NSMutableString string]initWithFormat:#"section=%#", section];
NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL: [NSURL URLWithString: url]];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[request setHTTPMethod: #"POST"];
//post section
[request setHTTPBody: myRequestData];
//store them in the array;
return [questions autorelease];
}
My php file:
<?php
//connect to database
function connect() {
$dbh = mysql_connect ("localhost", "abc1", "12345") or die ('I cannot connect to the database because: ' . mysql_error());
mysql_select_db("PDS", $dbh);
return $dbh;
}
//store posted data
if(isset($_POST['section'])){
$dbh = connect();
$section = $_POST['section'];
$query = mysql_query("SELECT * FROM QUESTIONS WHERE sectionId = $section;") or die("Error: " . mysql_error());;
$rows = array();
while($r = mysql_fetch_assoc($query)) {
$rows[] = $r;
}
echo '{"questions":'.json_encode($rows).'}';
mysql_close();
}
?>
I have built a model class (Question) in objective c which has the exact properties that each row element has in the rows associative array.
My questions are:
1) How can I read the echo'd JSON array elements and their relative attributes in objective C?
2) How can I create an array of Question objects and map each one to an element in the rows array?
3) What do I have to write in my method "+(NSMutableArray*)getQuestions:(NSString*)section from: (NSString*) url" to capture the reply from the php (the echo)?
EDIT:
Here is the output of the php:
http://dev.speechlink.co.uk/David/get_questionstest.php
UPDATE
Changed method to use ASIHTTPRequest - Cannot deserialise JSON string:
//method to
+(NSDictionary*)getQuestions:(NSString*)sectionId from: (NSString*) url{
NSDictionary *questions;
NSURL *link = [NSURL URLWithString:url];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:link];
[request setPostValue:sectionId forKey:#"section"];
NSError *error = [request error];
[request startAsynchronous];
if (!error) {
//NSString *response = [request responseString];
//store them in the dictionary
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
questions = [json objectFromJSONString];
NSLog(#"Data: %#", questions); //outputs Data: (null)
[json release];
[request release];
}else{
//UIAlertView to warn users there was an error
}
return questions;
}
Well, lets go through this one step at a time.
You can create a NSDictionary from JSON quite easily by using one of several different JSON parsing libraries. I really enjoy using JSONKit. Once you've imported JSONKit, into your project, you can do something like this:
NSString *url = #"http://dev.speechlink.co.uk/David/get_questionstest.php";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSDictionary *questions = [json objectFromJSONString];
[json release];
Now you have an array filled with the questions in your example. Now you can loop through this array and fill your data with the data in the array. Now lets be practical. It would be easier if you just had to manage one object instead of four for each question, wouldn't it? Lets make a class that contains one question each instance.
Interface:
#interface Question : NSObject {
NSString *questionId;
NSString *question;
NSString *questionNumber;
NSString *sectionId;
}
#property(copy)NSString *questionID;
#property(copy)NSString *question;
#property(copy)NSString *questionNumber;
#property(copy)NSString *sectionId;
#end
And implementation:
#implementation Question
#synthesize questionId, question, questionNumber, sectionID;
#end
Now that's just a basic example. Nothing fancy. Now you can loop through the array you had before and create "question" objects that contain each question's data. For my purposes, suppose you have a NSMutableArray named questionsArray that contain the questions you want to use. We'll loop through the dictionary and add the questions from the dictionary into the questionsArray array.
for (NSDictionary *q in questions) {
/* Create our Question object and populate it */
Question *question = [[Question alloc]init];
question.questionId = [q objectForKey:#"questionId"];
question.question = [q objectForKey:#"question"];
question.questionNumber = [q objectForKey:#"questionNumber"];
question.sectionId = [q objectForKey:#"sectionId"];
/* Add it to our question (mutable) array */
[questionsArray addObject:question];
[question release];
}
Tada! Now you have an array filled with Question objects. Any time you want to look at a property on any of the question objects, you can just simply access that property. For example, to grab the first question's number, you can just do this:
NSString *q1Number = [questionsArray objectAtIndex:0].questionNumber;
Please note this is all untested, as I don't have my compiler with me. It should get you started, though. =)
Edit: You were doing your request completely wrong. Try this:
+(NSDictionary*)getQuestions:(NSString*)sectionId from: (NSString*) url{
NSDictionary *questions = nil;
NSURL *link = [NSURL URLWithString:url];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:link];
[request setPostValue:sectionId forKey:#"section"];
NSError *error = [request error];
[request startSynchronous];
if (!error) {
NSData *response = [request responseData];
NSString *json = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
questions = [json objectFromJSONString];
[json release];
} else{
//UIAlertView to warn users there was an error
}
[request release];
return questions;
Take a look at Stig Brautaset's excellent JSON parser at GitHub.
And there are even a couple of sample projects included.
From your PHP, one would expect the parser to produce and array of NSDictionary objects. I'm not sure what you mean by question 2, but you can then iterate through the array and create custom "Question" objects with the NSDictionary values.
Hope this helps.
Didn't see that you added a third question. This is answered in the "TweetStream" example above. I would suggest that you use the NSURLConnectionDelegate methods as described by Apple.
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.
when I call
-(IBAction)goback:(id)sender
{
NSURL *xmlURL=[NSURL URLWithString:#"http://demo.komexa.com/sicherungsbereich.xml"];
NSURLRequest *request = [NSURLRequest requestWithURL:xmlURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:2];
NSURLResponse *theResponse;
NSError *theError;
NSData *myRequestResult = [NSURLConnection sendSynchronousRequest:request returningResponse:&theResponse error:&theError];
NSString *stringReply = (NSString *)[[NSString alloc] initWithData:myRequestResult encoding:NSUTF8StringEncoding];
NSLog(#"reply from server: %#", stringReply);
}
with the Iphone , on the simulator it loads the String everytime really from the internet.But on the devices, it caches the String, so even if the content of http://demo.komexa.com/sicherungsbereich.xml changes (you can do that by calling http://demo.komexa.com) the String does not automatically reload new data.
Have you got an Idea?
I have uploaded the Code here,because of formatting problems:
http://demo.komexa.com/problem.txt
just generate a random number and pass with your url , you get required result
e.g:
int randomNumber1 = 1 + arc4random()% 9;
NSString *rstr = [NSString stringWithFormat: #"%d",randomNumber1];
NSString *address = #"http://www.socialfactory.net/client-app/photovote/get_data.php";
NSString * nstr=[NSString stringWithFormat:#"%#?t=%#",address,rstr];
//NSURLRequest *theRequest=nil;
NSURLRequest * theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:nstr]];
NSURLResponse *resp = nil;
NSError *err = nil;
Maybe your Simulator and iPhone have different proxie settings. check this or try with the NSURLRequestReloadIgnoringLocalAndRemoteCacheData policy which also ignores intermediate cachings. See the docs here.