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.
Related
I am getting an error (well it doesn't shows, just crashes out of app, no info on console)
that seems to happen whenever i call the method Iterate from RXML's rootXML:
-(void)valueSearch {
//FIRST CONNECTION
NSString *serverAddress = #"http://www.commix.com.br/clientes/grupoglobo/apple/valor.xml";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverAddress]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
NSError *requestError;
NSURLResponse *urlResponse = nil;
response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
//SECOND CONNECTION - Just an encapsulated form of the first, since i use it in other parts
// of the code
response = [self requestWithParameters:#"valor.xml"];
//i just uncommented both. but actually only one (connection) runs.
//Creation of the rooXML so i can grab the info i need
RXMLElement *rootXML = [RXMLElement elementFromXMLData:response];
//This array is where i'll keep the info from the files.
//it`s deallocated at the end in dealloc
searchResult = [[NSMutableArray alloc] init];
//This is the culprit. Atleast it seems so, since putting NSLog before and after
//revealed so.
[rootXML iterate:#"valor" usingBlock: ^(RXMLElement *valor) {
NSLog(#"valor: %#", [valor child:#"nome"].text);
[searchResult addObject:[valor child:#"nome"].text];
}];
}
The thing is, when i comment the requestWithParametersand use the normal non-encapsulated style (//FIRST CONNECTION) i don't get errors. But if i use the second, when the program reaches [rootXML iterate: [...]]it crashes there without warning.
using RaptureXML: https://github.com/ZaBlanc/RaptureXML
It also happens in another part of the code:
-(void)vehicleSearch {
NSString *path = [[NSBundle mainBundle] pathForResource:#"idArray" ofType:#"plist"];
NSMutableArray *idArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSMutableString *serverAddress = (#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[idArray objectAtIndex:0]);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:serverAddress]
cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData
timeoutInterval:10];
NSError *requestError;
NSURLResponse *urlResponse = nil;
response = [NSURLConnection sendSynchronousRequest:request returningResponse:&urlResponse error:&requestError];
RXMLElement *rootXML = [RXMLElement elementFromXMLData:response];
searchResult = [[NSMutableArray alloc] init];
[rootXML iterate:#"modelo" usingBlock: ^(RXMLElement *modelo) {
NSLog(#"modelo: %#", [modelo child:#"nome"].text);
[searchResult addObject:[modelo child:#"nome"].text];
}];
[idArray release];
}
Happens at the same line [rootXML iterate:].
Sorry for leaks and stuff, i'm inexperienced (thats why i'm here), Thanks!
EDIT:
ACTUALLY the culprit is the line
NSMutableString *serverAddress = (#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[idArray objectAtIndex:0]);
if i pass the parameter directly, without variables, it works:
NSMutableString *serverAddress = (#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=4");
it shows correctly.
Are you sure that ,[idArray objectAtIndex:0] is an NSString?
Try to use
[NSString stringWithFormat:#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[idArray objectAtIndex:0]];`
Or even
[NSString stringWithFormat:#"http://www.commix.com.br/clientes/grupoglobo/apple/modelo.php?marc=%#",[[idArray objectAtIndex:0]stringValue]];
response = [self requestWithParameters:#"valor.xml"];
if response is a property use self.response otherwise you will have memory leak issues.
I have text file with 5 strings. I need to use NSURLConnection to get contnent of this file. But NSLog shows me, that 'dump' is empty. How can I transform the data from NSMutableData to NSArray. Arrays is because I need to show those 5 items in a TableView.
NSURLRequest *theRequest=[NSURLRequest
requestWithURL:[NSURL URLWithString:#"http://dl.dropbox.com/u/25105800/names.txt"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [NSMutableData data];
NSString *dump = [[NSString alloc] initWithData:receivedData
encoding:NSUTF8StringEncoding];
NSLog(#"data: %#", dump);
NSArray *outputArray=[dump componentsSeparatedByString:#"\n"];
self.namesArray = outputArray;
Thanks in advance. BTW URL works, you can see the file.
Here's how you implement this solution with a delegate:
In your .h file:
#interface MyClass : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
#property (nonatomic, retain) NSMutableData *receivedData;
#property (nonatomic, retain) NSArray *namesArray;
#end
In you .m file:
#implementation MyClass
#synthesize receivedData = _receivedData;
#synthesize namesArray = _namesArray;
- (id)init {
self = [super init];
if (self) {
self.receivedData = [NSMutableData data];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://dl.dropbox.com/u/25105800/names.txt"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
[connection start];
}
return self;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"Received response! %#", response);
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *dump = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSLog(#"data: %#", dump);
self.namesArray = [dump componentsSeparatedByString:#"\n"];
}
#end
If you don't want to use a delegate, you can use a synchronous call with NSURLConnection, like this:
NSURLRequest *theRequest=[NSURLRequest
requestWithURL:[NSURL URLWithString:#"http://dl.dropbox.com/u/25105800/names.txt"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSError *error = nil;
NSHTTPURLResponse *response = nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:theRequest response:&response error:&error];
if (error == nil) {
NSString *dump = [[NSString alloc] initWithData:receivedData
encoding:NSUTF8StringEncoding];
NSLog(#"data: %#", dump);
NSArray *outputArray=[dump componentsSeparatedByString:#"\n"];
self.namesArray = outputArray;
}
Just beware that this will not be running asynchronously. If you don't want it to run on the main thread and block your main thread/UI, consider using a separate thread to execute that code or use GCD.
You have to use the delegate, then save the received data into receivedData (which is of course empty right now.. you just initalized it.) and then you transform the data into a string, like you did it in your example. Have a look at NSURLConnectionDelegate
You need to implement the delegate methods for NSURLConnection to be notified of incoming data. You are using the asynchronous methods.
Also note that [NSMutableData data] just creates an empty data-object.. so you can't expect it to contain any data..
I suggest you read https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLConnection.html#//apple_ref/doc/uid/20001836-BAJEAIEE
(completely!)
I am making an iPhone app and I am loading information from a server. I send NSURLRequest to the server and get back a NSString value. This is working fine and the value I am getting back is the correct one. The problem is that when I try to add the value for the variable to a NSMutableDictionary I have made to store the values, it doesn't work. When I debug and look at the values of the NSMutableDictionary in Xcode it says 0 key/value pairs right after the line where I add the values. This is what my code looks like:
NSArray *varsToLoad = [fixedData objectForKey:#"varsToLoad"];
NSError *error;
for(NSString *var in varsToLoad){
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: someURL]];
[request setHTTPMethod:#"POST"];
NSMutableString *file = [NSMutableString stringWithString:#"file="];
NSString *file1 = [file stringByAppendingString:var];
NSString *file2 = [file1 stringByAppendingString:#".txt"];
[request setHTTPBody:[file2 dataUsingEncoding:NSUTF8StringEncoding]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
NSString *value = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
[varsLoaded setObject:value forKey:var];
}
varsLoaded is declared at the #implementation and is a NSMutableDictionary.
The problem may be that you haven't initialized your varsLoaded by saying self.varsLoaded = [NSMutableDictionary dictionary];
So you're adding objects to a non-existing dictionary, but you're not getting an exception because this is normal in objective-c :)
You should really check the response before processing any further. Wrap it in something like this:
if (response == nil) {
// Check for problems
}
else {
NSString *value = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
[varsLoaded setObject:value forKey:var];
}
Change the first line of your code from NSArray to NSMutableArray and see if you have better luck.
It doesn't matter that you declared your varsLoaded as a NSMutableDictionary somewhere else... in the local context of that code above, the compiler believes varsToLoad is a immutable array and probably isn't even compiling your setObject: forKey: line. You're not getting warnings in the build log or in the console while you're running??
-(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".