Leak problem with initWithContentsOfFile - xcode

My app is running good and looks ok. But after I ran it with Instruments, I found tons of leaks on it. There are several things seems not wrong like a code below. Is the code really have problems? Any single word will be helpful to me.
#interface GameData : NSObject
{
NSDictionary* _data;
NSDictionary* _localData;
}
#end
#implementation GameData
- (id) init
{
NSString* dataFilename = [[NSBundle mainBundle]pathForResource:#"GameData" ofType:#"plist"];
_data = [[NSDictionary alloc]initWithContentsOfFile:dataFilename]; // Leaks 48 bytes
NSString* localDataFilename = [[NSBundle mainBundle]pathForResource:#"GameData-Local" ofType:#"plist"];
_localData = [[NSDictionary alloc]initWithContentsOfFile:localDataFilename];
return self;
}
- (void) dealloc
{
[_data release];
[_localData release];
[super dealloc];
}
#end

Some operations may cause the frameworks to store static data structures that are never released. For instance, the implementation of -initWithContentsOfFile: may set up some internal settings the first time it's used which are then in place until the app is terminated, possibly for performance optimization reasons. This is not a real leak, but leak detection software will sometimes flag it as such. There's also the possibility that the framework itself has a bug that causes a memory leak but this is rare, especially for a well-established class like NSDictionary.
You're not leaking in your code, it's correct as far as I can tell. If your -dealloc method is being called (add a log statement to be sure) then you are fulfilling your part of the contract and any leak is not your fault.
It's probably worth using the ObjectAlloc instrument as this gives you a much better idea of what objects are allocated and are hanging around.

Related

ReactiveCocoa: RAC Objects still in memory after all signals complete

I am developing an application on iOS8 based on reactive cocoa. The application mainly does network operations.
I noticed that when all my signals complete and all signal references are nulled, i see a few RAC* objects still alive in the memory when i checked through instruments. Is this intended or is my code leaking memory? When i run the signal, there is an activity surge where i see a lot of RAC objects getting allocated and then it falls back to this state as shown the the below screen capture.
Every subsequent invocation ends in this same state. So i am not very worried about it btw.
http://imgur.com/sCL8Y3p
Thanks,
Those are all shared global instances that RAC uses so it doesn't need to allocate memory for every use of them. I'm sure there's a fancier word for that optimization, but I can't think what it is. For example, check out RACUnit:
+ (RACUnit *)defaultUnit {
static dispatch_once_t onceToken;
static RACUnit *defaultUnit = nil;
dispatch_once(&onceToken, ^{
defaultUnit = [[self alloc] init];
});
return defaultUnit;
}
Since all RACUnits are the same, RAC never bothers to make more than one instance of it. That's all you're seeing.

Why can't I use cocoa frameworks in different forked processes?

I was playing with the NSSound class to play a sound in a background process of my own so as to not block user input. I decided to call fork() but that is giving me problems. At the very moment the sound is allocated the forked process crashes. The funny thing is if I construct an example which only calls fork(), then the child process can call NSSound without problems, the crashes come only if I try to use other cocoa APIs before the fork()call. See this example with the crashme?() calls commented:
#import <AppKit/AppKit.h>
#import <math.h>
#define FILENAME \
"/System/Library/Components/CoreAudio.component/" \
"Contents/SharedSupport/SystemSounds/dock/drag to trash.aif"
void crashme1(void)
{
NSString *path = [[NSString alloc] initWithUTF8String:"false file"];
NSSound *sound = [[NSSound alloc]
initWithContentsOfFile:path byReference:YES];
}
void crashme2(void)
{
NSInteger tag = 0;
[[NSWorkspace sharedWorkspace]
performFileOperation:NSWorkspaceRecycleOperation
source:#"." destination:nil
files:[NSArray arrayWithObject:#"false file"] tag:&tag];
}
double playAif(void)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *path = [[NSString alloc] initWithUTF8String:FILENAME];
NSSound *sound = [[NSSound alloc]
initWithContentsOfFile:path byReference:YES];
[path release];
if (!sound) {
[pool release];
return -1;
}
const double ret = [sound duration];
[sound play];
[sound autorelease];
[pool release];
return ret;
}
int main(int argc, char *argv[])
{
//crashme1();
//crashme2();
int childpid = fork();
if (0 == childpid) {
printf("Starting playback\n");
double wait = playAif();
sleep(ceil(wait));
wait = playAif();
sleep(ceil(wait));
printf("Finished playback\n");
}
return 0;
}
When I run this from the commandline it works, but if I uncomment one of the calls to either crashme?() functions the playback in the forked process never starts. Is it because any of the Cocoa framework APIs are not async-signal safe? Is there any way to make the calls async signal safe by wrapping them somehow?
I'll quote the Leopard CoreFoundation Framework Release Notes. I don't know if they're still online, since Apple tends to replace rather than extend the Core Foundation release notes.
CoreFoundation and fork()
Due to the behavior of fork(), CoreFoundation cannot be used on the
child-side of fork(). If you fork(), you must follow that with an
exec*() call of some sort, and you should not use CoreFoundation APIs
within the child, before the exec*(). The applies to all higher-level
APIs which use CoreFoundation, and since you cannot know what those
higher-level APIs are doing, and whether they are using CoreFoundation
APIs, you should not use any higher-level APIs either. This includes
use of the daemon() function.
Additionally, per POSIX, only async-cancel-safe functions are safe to
use on the child side of fork(), so even use of lower-level
libSystem/BSD/UNIX APIs should be kept to a minimum, and ideally to
only async-cancel-safe functions.
This has always been true, and there have been notes made of this on
various Cocoa developer mailling lists in the past. But CoreFoundation
is taking some stronger measures now to "enforce" this limitation, so
we thought it would be worthwhile to add a release note to call this
out as well. A message is written to stderr when something uses API
which is definitely known not to be safe in CoreFoundation after
fork(). If file descriptor 2 has been closed, however, you will get no
message or notice, which is too bad. We tried to make processes
terminate in a very recognizable way, and did for a while and that was
very handy, but backwards binary compatibility prevented us from doing
so.
In other words, it has never been safe to do much of anything on the child side of a fork() other than exec a new program.
Besides the general POSIX prohibition, an oft-mentioned explanation is: a) pretty much all Cocoa programs are multithreaded these days, what with GCD and the like. B) when you fork(), only the calling thread survives into the child process; the other threads just vanish. Since those threads could have been manipulating shared resources, the child process can't rely on having sane state. For example, the malloc() implementation may have a lock to protect shared structures and that lock could have been held by one of the now-gone threads at the time of the fork(). So, if the remaining thread tries to allocate memory, it may hang indefinitely. Other shared data structures may simply be in a corrupted state. Etc.

Xcode instruments - fixing leaks

I have several memory leaks when testing my IOS application in xcode instruments. Can anyone see why the lines marked with "-->" are leaking?
--> CLLocationCoordinate2D newCoord = CLLocationCoordinate2DMake(latitude, longitude);
--> MapAnnotation* annotation = [[MapAnnotation alloc] initWithCoordinate:newCoord];
[mapView addAnnotation:annotation];
[annotation release];
also, I seem to have some structs which are leaking too;
--> double placeLat = [place.latitude doubleValue];
there is no pointer to the "placeLat" variable, so I can't release it? : /
thanks
Christy
Your code is correct, as much as you show of it.
One thing you have to keep in mind is that Leaks will show you the place where the leaked object is created, not the place where you do something wrong that will produce the leak.
This is an important difference. Indeed, it could well be, in the first case, that is the very annotation object which is leaked somewhere else in your code. You should check all the flow of execution that Instruments is showing you as stack trace.

RetainCount Memory not free'ed

I am stuck with the Cocoa Memory Managagment.
- (IBAction) createPush:(UIButton *)sender {
[create setEnabled:NO];
[release setEnabled:YES];
aLocation = [[Location alloc] init];
// Put some Example Stuff in the Class
aLocation.title = #"Apartment";
aLocation.street = #"Examplestreet 23";
aLocation.zip = #"12345";
aLocation.city = #"Exampletown";
aLocation.http = #"http://google.com";
aLocation.info = #"First Info Text";
aLocation.info2 = #"Second Info Text, not short as the first one";
aLocation.logoPath = #"http://google.de/nopic.jpg";
[aLocation.pictures addObject:#"http://google.de/nopic.jpg"];
[aLocation.pictures addObject:#"http://google.de/nopic.jpg"];
}
- (IBAction) releasePush:(UIButton *)sender {
[release setEnabled:NO];
[create setEnabled:YES];
[aLocation release];
aLocation = nil;
}
This Code works fine if I set or get Variables, but when I call the 'last' release (so the retain count is 0) it dealloc Method of aLocation gets called, but in Instruments Allocations you see that no memory is given back.
Here the Sources of Location:
http://homes.dnsalias.com/sof/Location.m
same Link with a '.h' instead of '.m' for the Header file (sorry its because of the Spaming Rule).
And the whole Project: http://homes.dnsalias.com/sof/Location.zip
Thanks for any help, where is my failure? Dennis
This Code works fine if I set or get
Variables, but when I call the 'last'
release (so the retain count is 0) it
dealloc Method of aLocation gets
called, but in Instruments Allocations
you see that no memory is given back.
What do you mean by "no memory is given back"?
Though oddly named, the memory management of aLocation is correct is the above code (assuming you have released it in dealloc as well).
Why doesn't memory use decrease when a single object is released?
(Paraphrased)
It is likely that your object is relatively tiny and, thus, that single deallocation falls below the ~20K or so needed to show up in Instruments.
If your app is crashing due to memory use issues, looking to a single deallocation is the wrong place to start. The first thing to do is to answer why your app is accreting memory and what is responsible for that growth.
Configure the Allocations instrument to only track live allocations. Then sort by total memory use. That'll show you what type of allocation is consuming the most memory. Start by reducing that.
Heapshot analysis can be very effective in these situations.
Additional Infos here because of maximum number of links and I have'nt the opportunity to post images...
What do you mean by "no memory is given back"?
I will show you the Instruments run, then it should be clear.
Screenshots from Instruments run
If want more Detail klick here for Instruments Run.
Your code is just fine. You are mistaken the output from Instruments. There is no Location object leaking.
For leaks, use the "Leaks" instrument. It won't fire. :-)

Leaking memory with Cocoa garbage collection

I've been beating my head against a wall trying to figure out how I had a memory leak in a garbage collected Cocoa app. (The memory usage in Activity Monitor would just grow and grow, and running the app using the GC Monitor instruments would also show an ever-growing graph.)
I eventually narrowed it down to a single pattern in my code. Data was being loaded into an NSData and then parsed by a C library (the data's bytes and length were passed into it). The C library has callbacks which would fire and return sub-string starting pointers and lengths (to avoid internal copying). However, for my purposes, I needed to turn them into NSStrings and keep them around awhile. I did this by using NSString's initWithBytes:length:encoding: method. I assumed that would copy the bytes and NSString would manage it appropriately, but something is going wrong because this leaks like crazy.
This code will "leak" or somehow trick the garbage collector:
- (void)meh
{
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"holmes" ofType:#"txt"]];
const int substrLength = 80;
for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) {
NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding];
[cocoaString length];
}
}
I can put this in timer and just watch memory usage go up and up with Activity Monitor as well as with the GC Monitor instrument. (holmes.txt is 594KB)
This isn't the best code in the world, but it shows the problem. (I'm running 10.6, the project is targeted for 10.5 - if that matters). I read over the garbage collection docs and noticed a number of possible pitfalls, but I don't think I'm doing anything obviously against the rules here. Doesn't hurt to ask, though. Thanks!
Project zip
Here's a pic of the object graph just growing and growing:
This is an unfortunate edge case. Please file a bug (http://bugreport.apple.com/) and attach your excellent minimal example.
The problem is two fold;
The main event loop isn't running and, thus, the collector isn't triggered via MEL activity. This leaves the collector doing its normal background only threshold based collections.
The data stores the data read from the file into a malloc'd buffer that is allocated from the malloc zone. Thus, the GC accounted allocation -- the NSData object itself -- is really tiny, but points to something really large (the malloc allocation). The end result is that the collector's threshold isn't hit and it doesn't collect. Obviously, improving this behavior is desired, but it is a hard problem.
This is a very easy bug to reproduce in a micro-benchmark or in isolation. In practice, there is typically enough going on that this problem won't happen. However, there may be certain cases where it does become problematic.
Change your code to this and the collector will collect the data objects. Note that you shouldn't use collectExhaustively often -- it does eat CPU.
- (void)meh
{
NSData *data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"holmes" ofType:#"txt"]];
const int substrLength = 80;
for (const char *substr = [data bytes]; substr-(const char *)[data bytes] < [data length]; substr += substrLength) {
NSString *cocoaString = [[NSString alloc] initWithBytes:substr length:substrLength encoding:NSUTF8StringEncoding];
[cocoaString length];
}
[data self];
[[NSGarbageCollector defaultCollector] collectExhaustively];
}
The [data self] keeps the data object alive after the last reference to it.

Resources