It seems like I keep asking the same questions, memory related. My current code works exactly as I intend it, but I cannot figure why I am showing a leak here in Instruments.
-(NSDate *)startTimeAndDate {
NSDate *dateToReturn = nil;
if (startTimeAndDate != nil) {
dateToReturn = [startTimeAndDate retain];
} else { //is currently nil, this will be the initial setting
//return default time if we have a working date
if (finishTimeAndDate != nil) {
dateToReturn = [[self dateFromDate:finishTimeAndDate withNewTime:defaultStartTime]retain];
} else {
//return the default time with today's date if we have nothing set as yet
dateToReturn = [[self dateFromDate:[NSDate date] withNewTime:defaultStartTime] retain];
}
//save the initial setting
self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
}
[startTimeAndDate release];
startTimeAndDate = dateToReturn;
return startTimeAndDate;
}
-(void)setStartTimeAndDate:(NSDate *)inStartTimeAndDate {
BOOL initialAssignment = NO;
if (startTimeAndDate == nil) {
initialAssignment = YES;
}
if (startTimeAndDate != inStartTimeAndDate) { //skip everything if passed object is same as current
//check that the start time is prior to finish only if finish time has been entered
NSDate *dateToSetStartTo = nil;
if (finishTimeAndDate != nil) {
if ([inStartTimeAndDate earlierDate:finishTimeAndDate] == inStartTimeAndDate) {
// use the new time, it is earlier than current finish time
dateToSetStartTo = [inStartTimeAndDate retain];
} else { //start time is not earlier then finish time
// the received entry is invalid, set start time to 1 default interval from finish
dateToSetStartTo = [[finishTimeAndDate dateByAddingTimeInterval:-self.defaultTimeInterval] retain];
}
} else { //finish time is nil
// use the new time without testing, nothing else is set
dateToSetStartTo = [inStartTimeAndDate retain];
}
[startTimeAndDate release];
startTimeAndDate = dateToSetStartTo;
}
if (initialAssignment) {
self.initialStartDateAndTime = [[self.startTimeAndDate copy] autorelease];
}
}
So far as I can see, I am balancing all retains with release or autorelease. The leak appears to be caused on the first pass only. I have a view controller, it creates my model (wherein this code lies) and sets a start date, nothing else is done at that point. If I close that view controller at that point, Instruments shows that I am leaving the date object as a leak.
I placed a NSLog to show retain count at dealloc and, sure enough, it has retain count of 2 before my final release is called, leaving a retain count of 1 when it should have been destroyed. It is always the same regardless if I close immediately after initialization or set and get a hundred times. retainCount is 2 prior to my final call to release in dealloc.
I have been looking at this all weekend and cannot figure where I've gone wrong.
To clarify, the initial call is to set the startTimeAndDate property. At that point all other fields are nil or 0 if not objects. That startTimeAndDate object appears to be the leaking object.
Firstly, can you describe the problem you are trying to solve with this code? I ask because it appears very complex and my initial thought is that simplification will not only clarify what you are doing, but is also likely to solve your leak as well.
Secondly, (and I may have this wrong), you only need to retain/release objects if you expect those objects to exist beyond the scope of the method, or you expect that they may be released by some code that you are claling in you method. Based on this, you appear to be over retaining and releasing in your code. I think you can remove a lot of it.
Again I may be wrong, but it appears that you will indeed leak. The reason I think so is this - on your first pass you retain some data in dateToReturn which is a local variable. Then you do
self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
But this is not releasing dateToReturn. Instead it is releasing the copy of dateToReturn. dateToReturn is still retained. Presuming that you intend to autorelease the copy because initialStartDateAndTime is set with retain, I think you should be doing:
self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
[dateToReturn release];
Of course, if you remove the extra retain/release's then this becomes simpler again.
The final thing I would suggest is around naming. The problem with code like this is that you have a number of methods and variables, all with very similar names. This can make it difficult to follow and lead to bugs. So ask yourself if you really need this many variables. And can you make your code more readible by changing some of the names.
Dam, ignore what I said. I just went through the code again and you're right. I think you are basically being burned by the complexity of the code. I found it quite difficult to follow, especially with the number of properties. I think what I would do at this stage is to copy the code to a unit test and run it from there. Then you can better test and debug it. I would recommend GHUnit if you do not already have unit testing in place.
The other thing that occurs to be is that there is code executing somewhere else in your program that is retaining the date. Therefore triggering the leak. For example if inStartTimeAndDate is coming in with a retain count of 1, but is not released by the code that called the setter then you could end up with startTimeAndDate with a retain of 2.
Having said that, here's my rewrite of the getter in an attempt to clarify whats going on:
-(NSDate *)startTimeAndDate {
// If we have it, bail out fast.
if (startTimeAndDate == nil) {
return startTimeAndDate;
}
// Is currently nil, this will be the initial setting
NSDate *dateToReturn = nil;
//return default time if we have a working date
if (finishTimeAndDate != nil) {
dateToReturn = [self dateFromDate:finishTimeAndDate withNewTime:defaultStartTime];
} else {
//return the default time with today's date if we have nothing set as yet
dateToReturn = [self dateFromDate:[NSDate date] withNewTime:defaultStartTime];
}
//save the initial setting
self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
startTimeAndDate = [dateToReturn retain];
return startTimeAndDate;
}
The main reason for this re-write was that it appeared that if there was a startTimeAndDate then the code was doing this:
dateToReturn = [startTimeAndDate retain];
...
[startTimeAndDate release];
startTimeAndDate = dateToReturn;
Which seemed a little pointless because it's effective doing a retain, release and self assignment. It would work, but there's less chance of a bug if we leave it out.
Related
I am trying to reason out in my head how to implement a thread-safe caching mechanism for a reference counted value with an API roughly like this: (Note: I'm using Objective-C syntax, but the problem is not language-specific)
typedef id (^InvalidatingLazyGenerator)();
#interface InvalidatingLazyObject : NSObject
- (id)initWithGenerator: (InvalidatingLazyGenerator)generator;
#property (readonly) id value;
- (void)invalidate;
#end
When someone requests -value, if it has an existing cached value, it should return a -retain/-autoreleased version of that value. If it doesn't have a value, or if the value isn't valid, it should generate one using a generation block passed in at init time, then it should cache that value for any future reads until someone calls -invalidate.
Let's assume we don't care if the generator block is called multiple times (i.e. a second reader arrives while the first reader is in the generator block), as long as the objects it returns aren't leaked when that happens. A first pass, non-wait-free implementation of this might look something like:
- (id)value
{
id retVal = nil;
#synchronized(self)
{
retVal = [mValue retain];
}
if (!retVal)
{
retVal = [[mGenerator() retain] retain]; // Once for the ivar and once for the return value
id oldVal = nil;
#synchronized(self)
{
oldVal = mValue;
mValue = retVal;
}
[oldVal release];
}
return [retVal autorelease];
}
- (void)invalidate
{
id val = nil;
#synchronized(self)
{
val = mValue;
mValue = nil;
}
[val release];
}
Naturally, this results in crappy read performance because concurrent reads are serialized by the lock. A reader/writer lock improves things from this, but is still quite slow in the read path. The performance goal here is for cached reads to be as fast as possible (hopefully lock-free). It's OK for reads to be slow if we have to calculate a new value, and it's OK for -invalidate to be slow.
So... I am trying to figure out a way to make reads lock/wait-free. My first (flawed - see below) thought involved adding an invalidation counter whose value is atomically, monotonically incremented and read using memory barriers. It looked like this:
- (id)value
{
// I think we don't need a memory barrier before this first read, because
// a stale read of the count can only cause us to generate a value unnecessarily,
// but should never cause us to return a stale value.
const int64_t startCount = mWriteCount;
id retVal = [mValue retain];
OSMemoryBarrier(); // But we definitely want a "fresh" read here.
const int64_t endCount = mWriteCount;
if (retVal && startCount == endCount)
{
return [retVal autorelease];
}
// Now we're in the slow path
retVal = [mGenerator() retain]; // we assume generator has given us an autoreleased object
#synchronized(self)
{
mValue = retVal;
OSAtomicIncrement64Barrier(&mWriteCount);
}
return retVal;
}
- (void)invalidate
{
id value = nil;
#synchronized(self)
{
value = mValue;
mValue = nil;
OSAtomicIncrement64Barrier(&mWriteCount);
}
[value release];
}
But I can already see problems here. For instance, the [mValue retain] in the read path: we need the value to be retained, but it's possible that in the time between the read of mValue and the call to -retain another thread could -invalidate, causing the value to have been dealloc'ed by the time the retain call is made. So this approach won't work as is. There may be other problems too.
Has anyone already worked something like this out and care to share? Or have a pointer to something similar in the wild?
I ended up taking this approach to the problem: Read-Copy-Update. It seems to work quite well -- the read performance is 50x faster than the lock-based approach (but that's hardly surprising.)
This question already has an answer here:
Self managing/releasing objects reported as "Potential leak of object" under Xcode -> Product -> Analyse
(1 answer)
Closed 9 years ago.
I am almost certain that I don't have a leak in this code, yet the Xcode analyzer reports that there is a "potential" leak (Xcode 4.6.1).
+ (MySHA1hash *)sha1HashWithHashBytes:(unsigned char *)hash length:(unsigned int)length;
{
return [[[MySHA1hash alloc] initWithHashBytes:hash length:length] autorelease];
}
If the problem is that Xcode is reporting a false positive, I would like to figure out how to structure the code in a way to silence the warning.
It is also the possible that I am leaking in a way I don't understand, but If someone can see how I am actually leaking I would be glad to get that feedback as well.
This must have something to do with the init functions I call, because if I simply replace initWithHashBytes with init, then the leak is no longer reported. To that end I also include the body of initWithHashBytes.
- (id)initWithHashBytes:(unsigned char *)hash length:(unsigned int)length
{
if (hash != nil && length <= SHA_DIGEST_LENGTH) {
NSData *data = [NSData dataWithBytes:hash length:length];
self = [self initWithHash:data];
}
else {
self = nil;
}
return self;
}
- (id)initWithHash:(NSData *)hash
{
if ([hash length] <= SHA_DIGEST_LENGTH && (self = [super init]) != nil) {
finished = YES;
[hash getBytes:sha_Result];
hashValue = [NSNumber numberWithInt:[hash hash]];
}
else {
self = nil;
}
return self;
}
The line
self = nil;
in initWithHashBytes: (and initWithHash:) is the issue. You are allocating an object, but if you return nil from from initWithHashBytes:, that object will be leaked, because you'll call autorelease on nil rather than on the object you allocated.
Release self before you return nil and all should be good.
In this particular case there was obviously an error that needed to be fixed, but I have seen at times a need to suppress warnings that are completely understood to be non problems (i.e. a leak reported that is not actually a leak).
This is what I expected to need to do here, but it turned out that there was an actual leak. So I am glad it got fixed. I immediately found another problem that was a clear an unmistakable "false positive" (I know that the error is reported as a "potential leak" so in reality it isn't a false positive, but it doesn't mean I want to see it in the report every time I run the analyzer).
Because of this I still had the question of how to suppress these warnings. It turns out you can easily wrap code that you want the analyzer to bypass in a ifdef check for __clang_analyzer.
#ifndef __clang_analyzer__
... code you want to ignore ...
#endif
There was a very good write up on this here.
You are missing a [self release] before self = nil.
The object you get from alloc has a reference count of +1 which needs to be balanced by a call to release or autorelease. For the case where you return your actual object from sha1HashWithHashBytes:length: the autorelease in that class method takes care of everything.
For the case you return nil your init method is the last one that has a reference to that allocated object so it has to release it.
I have been working on iOS project, which used data downloading in background. Well, honestly say - implementation wasn't the best one (and was changed later to remove such nonobvious pattern), and I've got some problems with not-deallocated objects, and can't say, that docs gave me a clear understanding of what's going on. Code, demonstrating common idea:
- (void)loadModelAtIndex:(NSUInteger)index {
Model *model = [self modelAtIndex:index];
if (model) {
model.index = index;
[self performSelectorInBackgroundThread:#selector(loadModelInBackgroundThread) withObject:model]
}
}
- (void)loadModelInBackgroundThread:(Model *)model {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
model.data = [NSData dataWithContentsOfURL:model.url];
//and some other changes can be here
[self performSelectorOnMainThread:#selector(modelDidLoad) withObject:model waitUntilDone:NO]'
[pool drain];
}
- (void)modelDidLoad:(Model *)model {
[self saveModel:model atIndex:model.index];
[self loadModelAtIndex:model.index + 1];
}
Well, almost all time it has been working as expected. Except if caller has beed deallocated while downloading in background - Model object stayed in memory, without being released (I've got growing memory at this point).
Can anyone explain me, what will happen in case of deallocation, while background thread is running? I'm not sure this code can be suitable at all, but still interested. Any modification will make things run well?
Except if caller has beed deallocated while downloading in background - Model object stayed in memory
the performSelector | …Thread… calls retain their arguments. If your objects are deallocated in that time, the problem lies elsewhere. Run with zombies enabled to locate it - you can record every reference count.
NSMutableArray *m_res = [NSMutableArray arrayWithCapacity:ticks];
double t = lo_t;
while (t <= hi_t) {
[m_res addObject:[NSDecimalNumber decimalNumberWithDecimal:
[[NSNumber numberWithDouble:t] decimalValue]
]];
t += delta_t;
}
return [[NSArray arrayWithArray:m_res] retain];
It is supposed to return a persistent NSArray containing some values. I plan to call release on it when it is no longer needed. Is it ok or there's some bug, because when I call function containing this code my program stops working (and it's a memory issue not endless loop).
The code is safe (ie won't crash) but the last line is incorrect.
The arrayWithArray is doing nothing useful and the retain is a leak.
It should be
return m_res;
If you hava a crash the cause is elsewhere.
I am checking Day objects in my schedule Dictionary and want to know what to do about the Day pointer when I'm done. Build and Analyze doesn't complain about it, but it just sits there taunting me. I feel like it's just sitting there when the function finishes and maybe even a new one gets created each time through the loop. Also, when releasing day each time through the loop I end up releasing the original object. Any ideas?
- (NSUInteger) showsInTheNext:(NSUInteger)days {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyyLLdd"];
if(days == 0) days = 91;
NSUInteger shows = 0;
for (NSUInteger x = 0; x < days; x++)
{
Day *day = [self.schedule objectForKey:[dateFormatter stringFromDate:[self.date addTimeInterval:60*60*24*x]]];
if((day != nil) && ([day.type isEqualToString:#"Show"])) shows++;
//[day release];
}
[dateFormatter release];
return shows;
}
The NSDictionary retains the NSObject subclass when it is added to the NSDictionary. When you fetch it back out no additional retain message is sent, so you should discard the pointer without sending a release message.
If the object is removed from the dictionary, the NSDictionary implementation will send a release message to the object being removed.
You can verify this by doing debug-level checks of the retainCount to make sure it doesn't change.
You are simply pulling an instance of Day that already exists from your schedule dictionary. You don't need to (and shouldn't) release it when you are done unless you are removing it from the dictionary. Nowhere in your loop is a new instance of Day being created.