Reputation: 315
I have a small project and I noticed that I have a lot of leaks in my code. So I started to use autorelease. But sometimes, I just cannot get rid of leaks or using autorelease causes analyzer to tell that there are too many autoreleases. Here is a function returning class attributedTitle containing font, shadow and alignment with current second. I've been playing with it awhile trying to realize what I am doing wrong. Could someone take a look at it and tell me where I have gone wrong?
- (NSAttributedString *)attributedTitle {
NSMutableParagraphStyle *pStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
[pStyle setAlignment: NSCenterTextAlignment];
NSShadow *pShadow = [[[NSShadow alloc] init] autorelease];
[pShadow setShadowColor: [NSColor colorWithSRGBRed:0.11 green: 0.11 blue:0.11 alpha: 0.67]];
[pShadow setShadowBlurRadius: 1.0];
[pShadow setShadowOffset: NSMakeSize(0,1)];
[pShadow set];
NSMutableDictionary *attributes = [[[[NSMutableDictionary alloc] initWithObjectsAndKeys:
[NSFont fontWithName: @"Arial Bold" size: 11], NSFontAttributeName,
[NSColor colorWithDeviceWhite: 1.0 alpha: 0.83], NSForegroundColorAttributeName,
pStyle, NSParagraphStyleAttributeName,
pShadow, NSShadowAttributeName,
nil] mutableCopy] autorelease];
NSString *text = [[[NSString alloc] initWithFormat: @"%d", [[[[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar]
components: NSSecondCalendarUnit fromDate: [[NSDate alloc] init]]second]] autorelease];
NSAttributedString *result = [[[NSAttributedString alloc] initWithString: text attributes: attributes] autorelease];
return result;
}
At first, this was shortened a lot - I didn't use variables if I was able to not, but I added variables and it became much longer with that - so this is actually just a example of leaks & autoreleases.
I am pretty sure this could be done much simpler as well but right now I am concerned about correct use of autorelease and how to get rid of leaks.. And why I have leaks (I've got a lot more of code as well that I can fix if I realize what's wrong..)
Okay, I got it sorted out.. Not leaking - shortened..
- (NSAttributedString *)attributedTitle {
NSMutableParagraphStyle *pStyle = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
[pStyle setAlignment: NSCenterTextAlignment];
NSShadow *pShadow = [[[NSShadow alloc] init] autorelease];
[pShadow setShadowColor: [NSColor colorWithSRGBRed:0.11 green: 0.11 blue:0.11 alpha: 0.67]];
[pShadow setShadowBlurRadius: 1.0];
[pShadow setShadowOffset: NSMakeSize(0,1)];
[pShadow set];
NSDictionary *attributes = [[[NSDictionary alloc] initWithObjectsAndKeys:
[NSFont fontWithName: @"Arial Bold" size: 11], NSFontAttributeName,
[NSColor colorWithDeviceWhite: 1.0 alpha: 0.83], NSForegroundColorAttributeName,
pStyle, NSParagraphStyleAttributeName,
pShadow, NSShadowAttributeName,
nil] autorelease];
NSDateComponents *dc = [[[[NSCalendar alloc] initWithCalendarIdentifier: NSGregorianCalendar] autorelease]
components: NSSecondCalendarUnit fromDate: [[[NSDate alloc] init] autorelease]];
return [[[NSAttributedString alloc] initWithString: [[[NSString alloc] initWithFormat: @"%d", [dc second]] autorelease] attributes: attributes] autorelease];
}
But what about this then?
SInt32 CFWeeksInYear(NSUInteger year)
{
NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
[dateFormat setDateFormat:@"dd.MM.YYYY"];
NSDate *tempMonth = [[[NSDate alloc] initWithString: [[[NSString alloc] initWithFormat: @"31.12.%ld", year] autorelease]] autorelease];
SInt32 result = CFAbsoluteTimeGetWeekOfYear([tempMonth timeIntervalSinceReferenceDate], CFTimeZoneCopyDefault());
return result;
}
SInt32 CFWeekOfYear(CFGregorianDate tempMonth)
{
CFAbsoluteTime tempDate = CFGregorianDateGetAbsoluteTime (tempMonth, CFTimeZoneCopyDefault());
return CFAbsoluteTimeGetWeekOfYear(tempDate, CFTimeZoneCopyDefault());
}
It leaks on:
SInt32 result = CFAbsoluteTimeGetWeekOfYear([tempMonth timeIntervalSinceReferenceDate], CFTimeZoneCopyDefault());
and:
CFAbsoluteTime tempDate = CFGregorianDateGetAbsoluteTime (tempMonth, CFTimeZoneCopyDefault());
and even on:
return CFAbsoluteTimeGetWeekOfYear(tempDate, CFTimeZoneCopyDefault());
Almost like it would have something to do with CFTimeZoneCopyDefault() but it shouldn't be returning a pointer..
Upvotes: 0
Views: 1132
Reputation: 6036
You maintain that CFTimeZoneCopyDefault
"shouldn't be returning a pointer.." but it is.
CFTimeZoneCopyDefault
returns a CFTimeZoneRef
. Let's find out what that is:
typedef const struct __CFTimeZone * CFTimeZoneRef;
Yep, it's a pointer.
This pointer isn't a reference to a Cocoa object, but it's a reference to allocated memory nonetheless. We know from the function naming conventions in Core Foundation that any CF function with the word 'copy' in it is allocating memory. That means that it's up to you to ensure that memory is released when you're done using it.
One way of releasing that memory* is to use the Core Foundation function CFRelease()
. With that in mind, let's revisit a bit of your code:
SInt32 CFWeekOfYear(CFGregorianDate tempMonth)
{
CFAbsoluteTime tempDate = CFGregorianDateGetAbsoluteTime (tempMonth, CFTimeZoneCopyDefault());
return CFAbsoluteTimeGetWeekOfYear(tempDate, CFTimeZoneCopyDefault());
}
We can keep a reference to the CFTimeZoneRef
, and then dispose of it when we're done:
SInt32 CFWeekOfYear(CFGregorianDate tempMonth)
{
CFTimeZoneRef tzRef = CFTimeZoneCopyDefault();
CFAbsoluteTime tempDate = CFGregorianDateGetAbsoluteTime (tempMonth, tzRef);
SInt32 result = CFAbsoluteTimeGetWeekOfYear(tempDate, tzRef);
CFRelease(tzRef);
return result;
}
Of course, real applications check for NULL responses (and the like), but you get the idea.
About autorelease: It's not a panacea for memory management issues. In your examples, you're doing all right, but I've seen badly designed applications exhaust physical RAM because the autorelease pool had grown so large. Be careful relying on autoreleased objects in places like loops and recursive methods; you can allocate your own autorelease pools (and release them) judiciously to avoid this kind of problem. In this forum, I can only be so verbose, so I recommend reading:
Good luck to you in your endeavors.
*CFTimeZone
is "toll-free bridged" with its Cocoa counterpart NSTimeZone
, which means that you could send the release
message to it (cast to NSTimeZone *
) as well.
Upvotes: 2
Reputation: 315
This is how you release CF's..
CFTimeZoneRef tz = CFTimeZoneCopyDefault();
/* Do what you need to do with it */
CFRelease(tz);
Upvotes: 0