My application uses bookmarks to retain access to files in sandboxed environment.
Every time i use
NSData *bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&error];
or
NSURL *resolvedURL = [NSURL URLByResolvingBookmarkData:storedBookmark options:nil relativeToURL:nil bookmarkDataIsStale:FALSE error:&resolveError ];
memory heap grows with non-objects allocations which are not deallocated later.
I am using ARC. #autorelease blocks makes no difference. Instruments showing no memory leaks.
How to get rid from these allocations? Thanks.
please use the pair of:
[url startAccessingSecurityScopedResource];
...
[url stopAccessingSecurityScopedResource];
Related
I'm developing an App that downloads and parses around 40k json files from an API. Due to the structure of my program, i don't parse them immediately, but save them in an NSMutable array. Each json is around 1KB, most of them even less. If my calculation is correct, that should produce around 40MB (+ some overhead) of allocated memory. But when i run the App, the memory usage climbs up to over 4GB.
Am i leaking something here? Since i use garbage collection, i should not need to dealloc anything, right? Or is my calculation simply wrong?
Here my code:
- (void) loadItemsForIds:(NSArray*)idList {
for (NSNumber* n in idList) {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://someapi.com/somejson.json?id=%#", n]];
NSData* response = [NSData dataWithContentsOfURL:url];
NSDictionary *loadedData = [NSJSONSerialization JSONObjectWithData:response options:0 error:nil];
#synchronized(self.updateData) {
[self.updateData addObject:loadedData];
}
[self performSelectorOnMainThread:#selector(progressLoadingInterface:) withObject:[loadedData valueForKey:#"name"] waitUntilDone:NO];
};
}
Edit:
Found out that the problem even exists, if i don't save the data into the array. After some research i stumbled upon this question which deals with the same problem: iPhone - Memory Leak - NSData dataWithContentsOfUrl & UIWebView
My question might need to be deleted then.
[NSJSONSerialization JSONObjectWithData:response options:0 error:nil] parses the JSON data into objects. This will take up more memory than the 1K of JSON data.
In addition, NSString is likely storing all your string data as unichar, which is a 16-bit representation: double the size of the UTF-8 encoding assumed in the JSON data.
Depending on the contents of the JSON, I can believe that 1K of JSON data could become 10K of objects.
But this is all guess work. Use Instruments (Product > Profile) to check memory usage. That will give you a better idea what objects are taking up all the memory.
you never release memory between the 40k iterations.
=> You never give anybody the chance to do so either (arc/gc)
wrap the loop body in an #autoreleasepool {...} so memory is drained
for (NSNumber* n in idList) {
#autoreleasepool {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://someapi.com/somejson.json?id=%#", n]];
NSData* response = [NSData dataWithContentsOfURL:url];
NSDictionary *loadedData = [NSJSONSerialization JSONObjectWithData:response options:0 error:nil];
#synchronized(self.updateData) {
[self.updateData addObject:loadedData];
}
[self performSelectorOnMainThread:#selector(progressLoadingInterface:) withObject:[loadedData valueForKey:#"name"] waitUntilDone:NO];
}
}
I am developing application which required image caching. For doing this, I am using JMImageCache library. It is work fine for caching. But It can not release memory occupied by
following line.
[NSData dataWithContentsOfFile]
Here, is function which content code for cache image from disk.
- (UIImage *) imageFromDiskForURL:(NSString *)url {
NSData *data = [NSData dataWithContentsOfFile:cachePathForURL(url) options:0 error:NULL];
UIImage *i = [[[UIImage alloc] initWithData:data] autorelease];
data = nil;
[data release];
return i;
}
I have check it with instruments and it alloc 2.34 MB each time.
data = nil;
[data release];
Why do you expect this at all to work? Why should this release the original data? You're sending the release message to nil, which is a no-op.
Furthermore, if you don't create the object using alloc or copy, then it's autoreleased. That means if you release it once more, it will be overreleased and most likely your app is going to crash. What you need is:
One. Wrap the method call in an explicit autorelease pool:
- (UIImage *)imageFromDiskForURL:(NSString *)url
{
UIImage *i;
#autoreleasepool {
NSData *data = [NSData dataWithContentsOfFile:cachePathForURL(url) options:0 error:NULL];
i = [[UIImage alloc] initWithData:data];
}
return [i autorelease];
}
Two, alloc-init or manually release the data object:
- (UIImage *)imageFromDiskForURL:(NSString *)url
{
NSData *data = [[NSData alloc] initWithContentsOfFile:cachePathForURL(url) options:0 error:NULL];
UIImage *i = [[[UIImage alloc] initWithData:data] autorelease];
[data release];
return i;
}
alter the sequence nil and release
[data release];
data = nil;
and for clearing the cache use following delegate methods
[[JMImageCache sharedCache] removeAllObjects];
[[JMImageCache sharedCache] removeImageForURL:#"http://dundermifflin.com/i/MichaelScott.png"];
read the read me file of library
https://github.com/jakemarsh/JMImageCache/blob/master/README.markdown
What you are trying to do cannot work due to the way how UIImage uses a data you pass it. The data object is retained by the image, or more precisely by a CGImageSource that the UIImage has internally. From this data it is able to decompress and create the image any time. There is an option on CGImageSource to also keep around the decompressed data, but UIImage does not use that because it optimized for small UI graphics.
One thing that you can do to alleviate memory pressure is to not load the entire NSData into memory, but to memory-map it instead. This makes creation or recreation of the image a tad slower, but the created NSData object is very small in comparison.
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
//responseData = nil;
NSMutableDictionary* json =[[responseString JSONValue] retain];
[responseString release];
NSLog(#"Sample message %d",[json retainCount]);
Here m getting retain count is 2, can any one help in this how can i track, this retain count.
The problem is when i write the following code the app is crashing.
[json release];
app crashes.
retainCount is useless. Don't call it.
You can't use retainCount to try and determine when something needs to be released.
In that code, it isn't clear why it is crashing. Post the backtrace of the crash.
In the method below I'm receiving "EXC_BAD_ACCESS" on the line containing the "urlString" variable. My research suggests that this error occurs when the program sends a message to a variable that has already been released. However since I'm using ARC I'm not manually releasing memory. How can I prevent ARC from releasing this variable too soon?
-(NSMutableArray *)fetchImages:(NSInteger *)count {
//prepare URL request
NSString *urlString = [NSString stringWithFormat:#"http://foo.example.com/image?quantity=%#", count];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
//Perform request and get JSON as a NSData object
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
//Parse the retrieved JSON to an NSArray
NSError *jsonParsingError = nil;
NSArray *imageFileData = [NSJSONSerialization JSONObjectWithData:response options:0 error:&jsonParsingError];
//Create an Array to store image names
NSMutableArray *imageFileNameArray;
//Iterate through the data
for(int i=0; i<[imageFileData count];i++)
{
[imageFileNameArray addObject:[imageFileData objectAtIndex:i]];
}
return imageFileNameArray;
}
Your problem has nothing to do with ARC. NSInteger isn't a class, so you don't want to be using the %# format. %# is going to send a description method to what the system thinks is an object, but when it turns out not to be one - CRASH. To solve your problem, you have two options:
You might want:
NSString *urlString =
[NSString stringWithFormat:#"http://foo.example.com/image?quantity=%d",
*count];
Make sure the count pointer is valid first!
You might need to change your method signature to be:
-(NSMutableArray *)fetchImages:(NSInteger)count;
and then change the urlString line as follows:
NSString *urlString =
[NSString stringWithFormat:#"http://foo.example.com/image?quantity=%d",
count];
You'll also need to fix all of the callers to match the new signature.
The second option seems more "normal" to me, but without more of your program it's impossible to be more specific.
you also may want to alloc and init the
NSMutableArray *imageFileNameArray;
before adding objects to it, otherwise you'll keep crashing. So you'd have
//Create an Array to store image names
NSMutableArray *imageFileNameArray = [[NSMutableArray alloc] init];
I am parsing some XML using TouchXML and I am getting a crash -EXC_BAD_ACCESS. What I found out through trial and error was that if I don't release my CXMLDocument (which I allocate), then everything is fine. Here's my code:
- (NSArray *)getLookUps {
//Do some stuff and then...
NSData *tempData = [NSURLConnection sendSynchronousRequest:request
returningResponse:nil
error:nil];
CXMLDocument *xmlDoc = [[CXMLDocument alloc] initWithData:tempData options:0 error:nil];
NSDictionary *mappings = [NSDictionary dictionaryWithObject:#"http://****/****"
forKey:#"****"];
NSLog(#"%#", [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding]);
NSArray *orders = [[xmlDoc rootElement] nodesForXPath:#"//****:Unit"
namespaceMappings:mappings
error:nil];
NSMutableArray *units = [NSMutableArray arrayWithCapacity:200];
for (CXMLElement *order in orders) {
NSArray *nodes = [order children];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:[nodes count]];
for (CXMLElement *node in nodes) {
[dictionary setObject:[node stringValue] forKey:[node name]];
}
[units addObject:dictionary];
}
//[xmlDoc release];
return units;
}
See on the 2nd last line, [xmlDoc release]. I have commented that out, because it crashes if I don't. What am I doing wrong? Thanks.
You probably need to retain your dictionary object otherwise it will also be released when you release the parser. Try changing [units addObject:dictionary]; to [units addObject:[dictionary retain]];.
Another idea is to set your xmlDoc pointer to autorelease:
CXMLDocument *xmlDoc = [[[CXMLDocument alloc] initWithData:tempData options:0 error:nil] autorelease];
This bug was reported and is flagged as fixed in the newer versions of the library.
http://code.google.com/p/touchcode/issues/detail?id=35
I haven't tested to see if it is actually fixed, a comment at that URL suggests that it isn't.
In my opinion, this library should be avoided altogether. For iOS apps, use libxml2 for several reasons:
It's tested and tried, through and through
It's fast and efficient
Building a node based representation of your XML might make it easier to code with, but it wastes memory as you always have the entire document in memory. You probably have it more than once while parsing. You should instead design your code to work with the libxml2 approach. You'll agree once you start parsing documents of substantial size.
I used TouchXML quite often, and (fortunately?) I did not have this problem up to now, but it just happened ...
I posted a solution here:
Memory crash using [CXMLNode nodesForXPath] with namespace mappings
I observed in TouchXML Class "CXMLDocument" we have the following handling in "dealloc" method.
- (void)dealloc
{
// Fix for #35 http://code.google.com/p/touchcode/issues/detail?id=35 -- clear up the node objects first (inside a pool so I _know_ they're cleared) and then freeing the document
#autoreleasepool {
nodePool = NULL;
}
//
xmlUnlinkNode(_node);
xmlFreeDoc((xmlDocPtr)_node);
_node = NULL;
}
I am not sure why we are using "autoreleasepool" in "dealloc". Is this is standard coding? Correct me if I am wrong.