-(IBAction)registerUpdate:(id)sender {
HTTPRequest* request = [[HTTPRequest alloc] initWithUrl:#"http://www.yahoo.com" delegate:self];
[request doRequest];
}
The HTTPRequest makes an asynchronous request and calls the onHTTPResponse method in the current class.
My question is do I have to release request? My guess is that I'm supposed to make it an instance variable?
[NSString stringWithFormat:#"Data received: %#", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]];
How would I release that string object, or should I assign it to a variable?
You release it with autorelease
[NSString stringWithFormat:#"Data received: %#", [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease]]
Related
I am trying to call a class method that takes a string and posts it to a site to receive a JSON response(among some other variables I have stored in the DataClass). I am stuck trying to return the data in the form of a response and can not at this point even NSLog the returned data. The question is, now that I have called my class method, how can the class method wait to return a response from an HTTP POST to return data? Once I return my JSON, I can expand it to a dictionary and process from there. Help is appreciated :)
Class Method:
//
// APISample.m
//
// Created by Sam on 1/6/13.
// Copyright (c) 2013 Sam. All rights reserved.
//
#import "APISample.h"
#import "DataClass.h"
#implementation APISample
#synthesize first_name = _first_name;
#synthesize last_name = _last_name;
#synthesize profile_pic_url = _profile_pic_url;
#synthesize responseData;
-(id)init
{
self = [super init];
return self;
NSLog(#"Loaded APISample and fetching");
}
+(id)getDataAboutUser:(NSString *)user_request_id;
{
DataClass *userdata=[DataClass getInstance];
NSLog(#"Loaded APISample and fetching %#", user_request_id);
NSMutableURLRequest *user_fetch_details = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://10.0.23.161/users/user_fetch_details.php"]];
[user_fetch_details setHTTPMethod:#"POST"];
NSMutableString *postString = [NSMutableString stringWithString:#"id=123"];
[postString appendString:#"&userrequest_id="];
[postString appendString:[userdata.str_userid copy]];
[postString appendString:#"&user_id="];
[postString appendString:[userdata.str_userid copy]];
[postString appendString:#"&identifier="];
[postString appendString:[userdata.str_identifier copy]];
[user_fetch_details setValue:[NSString stringWithFormat:#"%d", [postString length]] forHTTPHeaderField:#"Content-length"];
[user_fetch_details setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSURLConnection *connection=[[NSURLConnection alloc] initWithRequest:user_fetch_details delegate:self];
NSMutableData *responseData=[NSMutableData data];
[responseData appendData:[NSURLConnection connection:didReceiveData];
if (connection) {
// Create the NSMutableData that will hold
// the received data
// receivedData is declared as a method instance elsewhere
NSMutableData *responseData=[NSMutableData data];
} else {
// inform the user that the download could not be made
}
NSLog(#"Received Data %#", [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding]);
return [[NSString alloc] initWithData:responseData encoding:NSASCIIStringEncoding];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
NSString *receivedDataString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
if ([receivedDataString isEqualToString: #"error"]) {
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"An error has occured. The application will now exit. Unexpected Response!"
delegate:nil
cancelButtonTitle:#"Close"
otherButtonTitles:nil];
[errorAlert show];
exit(0);
}else{
NSError* error;
NSDictionary* json = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
NSString *firstnameResponse = [json objectForKey:#"first_name"];
NSString *lastnameResponse = [json objectForKey:#"last_name"];
NSString *profile_pic_urlResponse = [json objectForKey:#"profile_pic_url"];
NSLog(#"didReceiveData %# analysed " , firstnameResponse);
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading");
NSLog(#"Succeeded! Received %d bytes of data",[self.responseData length]);
}
#end
I receive no data in the log after "Received Data" and do not see my error messages. Thanks you
The design pattern you are describing is called a CallBack. You need to be notified of an event occurring at some point in the future. In objective-c there are 4 main forms of callbacks.
Target Action Pairing (this is what is used with buttons, and things of the like. "When this button is pressed notify my target, and tell them to execute this action")
Delegation (you are using a form of delegation in the code above with NSURLConnection. When you see the word 'delegate' i want you to think 'helper object'. You are saying, "hey NSURLConnection, when important events happen, i would like you to tell this delegate (helper object) about these events)
Notifications (these are used a lot when dealing with model objects changing)
and finally... the one i would recommend for your purposes...
Blocks.
A block is a very cool variable. Most variables hold data. A block is a variable which holds code to be executed at some point in the future. So in your situation you could pass a completion block along with your method getDataAboutUser:(NSString *)user_request_id. So it would look like this.
getDataAboutUser:(NSString*)string withCompletion:(void(^)(NSData *finishedData))cBlock
Store that cBlock as an instanceVar. Then when your NSURLConnection finishes downloading all its data, you will execute the cBlock, passing in the finished data as an argument.
Blocks are a fairly complicated things if you have not used them before, so i would reccomend taking 20 minutes and reading this.
Since you need your method to wait for a response before returning, you can use NSURLConnection's convenience class method sendSynchronousRequest to carry out a synchronous request instead of creating and managing an NSURLConnection instance asynchronously.
So instead of your [[NSURLConnection alloc] init...] line you can do this:
NSURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:user_fetch_details returningResponse:&response error:&error];
Following which you can immediately parse the JSON from responseData instead of doing that in the connection:didReceiveData delegate.
Edit: Just saw user698846's suggestion to modify your method signature to take a completion block. That's also a good and possibly cleaner way to approach your problem if you are at liberty to change your method signature (i.e. nobody is requiring your function to return synchronously). Either way, sendSynchronousRequest is possibly the easiest way out and there's no shame in it especially if there's nothing your app nor your user can do while waiting for the request to complete.
This is some code:
NSURLResponse *response = nil;
NSError *error = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:user_fetch_details returningResponse:&response error:&error];
I have made this so far. It's code that will make a json String request with an http Header. When i run this code i get no errors. But i get a Expression result unused warning. I should get a response from the web service after sending this http header.
code:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *nid = #"";
NSString *vocab = #"";
NSString *inturl = #"testoverview";
NSString *mail = #"chh#fbr.dk";
NSString *md5pw = #"4d57e7ef1b7c3f431aca424764e9d786";
NSDictionary *jsonDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
nid, #"nid",
vocab, #"vocab",
inturl, #"inturl",
mail, #"mail",
md5pw, #"md5pw",nil];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDictionary options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
if (!jsonData) {
NSLog(#"Got an error; %#", error);
} else if(jsonData) {
NSString *url = #"http://www.taenk.dk/services/mobile";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLCacheStorageAllowed timeoutInterval:30.0];
[request setValue:jsonString forHTTPHeaderField:#"X-FBR-App"];
[[NSURLConnection alloc] initWithRequest:request delegate:self]; <-- this line triggers the warning: "Expression result unused"
NSLog(#"jsonString %#", jsonString);
}
Can anybody clarify 2 things for me:
Does this trigger a response as soon as the request to the web service?
If yes, how do i print this result out?
You need to assign the result to a variable like
NSURLConnection *con = [[NSURLConnection alloc] initWithRequest:...
for later use (e.g. [con cancel];)
Then you should at least implement the delegate method connection:didFailWithError:. In the class reference I don't see the connection:didFinishLoading... anymore. Can you use the sendSynchronousRequest:returningResponse:error: instead, then you'll have the result, be it positive or negative.
This is how I retrieved the data (this version is without ARC):
- (void) connection :(NSURLConnection *)conn didReceiveData :(NSData *)data {
NSString *msg = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
[self checkAutoMailReply:msg];
[msg release];
}
msg contains the pure response data, no header or such.
-(void)messageSend:(NSString *)message;
{
NSLog(#"messageSend");
urlString = [[NSString alloc] initWithFormat:#"http://someaddress/message/send?from=%#&msg=%#&latitude=0&longitude=0",appDelegate.userName,message];
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithDictionary:[self request:urlString]];
NSLog(#"Dictionary response");
if ([dictionary count] > 0)
{
if ([[dictionary objectForKey:#"send"] isEqualToString:#"OK"] )
{
NSLog(#"envio de mensagem de %# Ok: %#",appDelegate.userName,message);
}
}
[urlString release];
[dictionary release];
}
Gives an error of -[__NSArrayM getObjects:andKeys:]: unrecognized selector sent to instance. After some testing with NSLogs, the line
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithDictionary:[self request:urlString]];
is the culprit, witch is calling this method:
-(NSDictionary *)request:(NSString *)requestString
{
url =[[NSURL alloc] initWithString:requestString];
request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
error = [[NSError alloc] init];
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[responseData retain];
NSString *tempString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSMutableDictionary *tempDict= [[[NSMutableDictionary alloc] init] autorelease];
if (request)
{
Parser *parser = [[Parser alloc] init];
tempDict = [parser readXMLString:tempString];
for (id key in tempDict)
{
NSLog(#"%# is %#",key,[tempDict objectForKey:key]);
}
}
[url release];
[error release];
[responseData release];
[tempString release];
return tempDict;
}
And it happens when the string of the message has spaces.
But it was not happening before.
I see a few peculiarities:
error = [[NSError alloc] init];
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
Usually, you simply do:
NSError *error = nil;
responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
The error variable will be filled with the address of an autoreleased error object if there is an error, otherwise it should remain nil. Do not allocate one yourself, since you could be releasing the wrong one (the one returned by the sendSynchronousRequest:etc. method). This could cause an over-release at the end of your method.
NSMutableDictionary *tempDict= [[[NSMutableDictionary alloc] init] autorelease];
if (request)
{
Parser *parser = [[Parser alloc] init];
tempDict = [parser readXMLString:tempString];
In the if-block, you are overwriting the pointer to the tempDict you just created. That is a memory leak (but not the cause of your problem). Update: the one you created is autoreleased. No leak.
You also don't release the Parser used in the if-block (and local to it).
You never check the value of error to see if actually an error occurred. As I said, you should set error to nil before the invocation of sendSynchronousRequest:etc. and then check if it is still nil, and if not, react accordingly:
if (error)
{
// error handling
}
What is the return type of [parser readXMLString: tempString];? Could it be an array and not a dictionary? E.g. an array of dictionaries?
Add an
NSLog(#"%#", tempDict);
in request:, before you return the tempDict. What does it show?
The getObjects:AndKeys: is probably called in -[NSMutableDictionary initWithDictionary:]. Apparently the real type of the dictionary returned by request: is not a dictionary, it is an array. See what I wrote above.
The culprit is the line tempDict = [parser readXMLString:tempString]. In fact, this means your previous creation of a [[[NSMutableDictionary alloc] init] autorelease] is pointless, as it will just be overwritten by the return value of [parser readXMLString:tempString]. In any case, it appears the -readXMLString: method is returning an NSArray instead of an NSDictionary.
I'm kinda new in finding memory leaks in objective c and how to fix them. I'm know how to use alloc/init/copy and release/retain but ( a least i think so :-) ) but i have some strange memory leaks in my IOS app.
-(void) sendStats {
// read the app settings
plistHandler *readData = [[plistHandler alloc] init];
[readData setPlistName:#"Settings"];
NSDictionary *settingsArray = [readData readPlist];
[readData release];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber * myNumber = [f numberFromString:[NSString stringWithFormat:#"%#", [settingsArray objectForKey:#"range"]]];
[f release];
int rangeForUrl;
if(myNumber != nil) {
rangeForUrl = [myNumber intValue];
} else {
rangeForUrl = 10;
}
// get uniqe device ID
UIDevice *getdev = [UIDevice currentDevice];
NSString *uniqueIdentifier = [getdev uniqueIdentifier];
NSString *deviceId = [NSString stringWithFormat:#"IOS-%#", uniqueIdentifier];
// get the unix timestamp
NSDate * past = [NSDate date];
NSTimeInterval oldTime = [past timeIntervalSince1970];
NSString * unixTime = [[[NSString alloc] initWithFormat:#"%0.0f", oldTime] autorelease];
// send the data with a post request to the API
HttpRequest *data = [[HttpRequest alloc] init];
data.postData = [NSString stringWithFormat:#"&device=%#&age=%#&gender=%#&latitude=%#&longitude=%#×tamp=%#", deviceId, [settingsArray objectForKey:#"age"], [settingsArray objectForKey:#"gender"], #"00", #"00", unixTime];
data.controller = #"sendDevice";
NSString *url = [NSString stringWithFormat:#"http://www..eu/api/send_device"];
[data loadHostName:url];
[data release];
//NSLog(#"string s: %#", data.postData);
}
This is the memory leak according to Xcode instruments => leaks:
Leaked Object # Address Size Responsible Library Responsible Frame
NSCFString, 0x16cb40 144 Bytes Foundation -[NSPlaceholderString initWithFormat:locale:arguments:]
This is the line with "data.postData = ..." in my code. Can someone help me out?
this is how I use postData in the class httpRequest:
- (void)loadHostName:(NSString *)hostName {
responseData = [[NSMutableData alloc] init];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#", hostName]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
if(authString) {
[request setValue:authString forHTTPHeaderField:#"Authorization"];
}
if([postData isKindOfClass:[NSString class]] && postData != #"") {
NSData *dataToPost = [postData dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d",[dataToPost length]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Current-Type"];
[request setHTTPBody:dataToPost];
}
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection release];
}
with of course:
NSString *postData;
#property (nonatomic, retain) NSString *postData;
and
#synthesize postData;
You need to release postData in dealloc.
looks like its fixed now. But i dont really understand how the dealloc works. I never did a
alloc/init on postData. Does this mean every object in my .h file like
postData need to be released in the dealloc in the .m file?
Speaking of properties, you need to release in dealloc all your properties not marked assign (along with any non-property instance variable you own). Non-assign properties own the object held by the backing instance variable so you need to relinquish ownership of it by sending it a release message in dealloc.
From "The Objective-C Programming Language":
Declared properties, along with the #synthesize directive, take the
place of accessor method declarations; when you synthesize a property,
the compiler creates accessor methods as needed. However, there is no
direct interaction between property declaration and the dealloc
method—properties are not automatically released for you. Declared
properties do, however, provide a useful way to cross-check the
implementation of your dealloc method: you can look for all the
property declarations in your header file and make sure that object
properties not marked assign are released, and those marked assign are
not released.
I would also recommend you to read the "Memory Management Programming Guide".
I'm using this code, but when profiling, it tells me I have a many memory leaks within response_error, request and _response variables.
I tried several places to put a release code of each variable used in function, but it keeps crashing with and without error message too. (most often it is EXC_BAD_ACCESS which points to memory access error)
I think it could be problem of NSURLConnection sendSynchronousRequest method, but I'm not sure.
Can somebody please give me an advice or place release blocks in right place of this code?
Thanks
NSString *request_url = [NSString stringWithFormat:#"http://www.server.com/api/arg1/%#/arg2/%#/arg3/%#",self._api_key,self._device_id,self._token];
NSURL *requestURL = [NSURL URLWithString:request_url];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:requestURL];
NSError *response_error = [[NSError alloc] init];
NSHTTPURLResponse *_response = [[NSHTTPURLResponse alloc] init];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&_response error:&response_error];
NSString *str_response = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
return [[str_response JSONValue] valueForKey:#"pairing"];
where variables are defined like
#interface MyClass : NSObject {
NSString *_device_id;
NSString *_token;
NSString *_api_key;
}
#property (nonatomic,retain) NSString *_device_id;
#property (nonatomic,retain) NSString *_api_key;
#property (nonatomic,retain) NSString *_token;
You are leaking _respone and response_error by needlessly allocating them. You are passing a pointer to your pointer to a method that will just change the pointer creating a leak. Also you need to autorelease str_response
NSError *response_error = nil; //Do not alloc/init
NSHTTPURLResponse *_response = nil; //Do not alloc/init
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:&_response error:&response_error];
NSString *str_response = [[[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding] autorelease];
return [[str_response JSONValue] valueForKey:#"pairing"];
If you are calling alloc/init and then not calling release or autorelease, odds are you are going to leak memory.