Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

MrFusion

macrumors 6502a
Original poster
Jun 8, 2005
613
0
West-Europe
The dashes in the title were supposed to be "vs", but I ran out of space.

First a general question: in your expert view, what is good coding practice when sharing code between Mac OS X and iOS projects?

Sometimes I see this on websites:
#if TARGET_OS_IPHONE (or similar)
do something
#else
do something else
#endif
Is this the best approach? To me it seems like every other line would duplicate itself until it's a gigantic mess of #if statements
Also, how or where do you tell xCode when to set TARGET_OS_IPHONE to 0 or 1?


Since the mac has cocoa, iOS has appkit and both have core graphics, custom drawing can also be done with core graphics. That should promote code sharing between a mac and iOS project, or not?

The code below can be found in the documentation or on google and it seems to work just fine. It deals with converting between CGColorRef and NSColor or UIColor. By sticking the conversion into a separate file and keeping the drawing code generic by using CGColorRef, it should minimize code duplication in the drawing routines of both projects, or not?

My question is about the last line:
Code:
return (CGColorRef)[(id)CGColorCreate(colorSpace, components) autorelease];
A corefoundation object is cast to an NSObject (?) and then autoreleased. Is this valid code or good practice?
I thought corefoundation objects could not be autoreleased.

Thanks for sharing your insight!



Code:
@implementation NSUserDefaults (UserDefaultsExtensions)

-(void) setColor:(NSColor *) aColor 
		  forKey:(NSString *) aKey
{
    NSData *theData = [NSArchiver archivedDataWithRootObject:aColor];
    [self setObject:theData 
			 forKey:aKey];
}

-(NSColor *) colorForKey:(NSString *) aKey
{
    NSColor *theColor = nil;
    NSData *theData = [self dataForKey:aKey];
    if (theData != nil)
        theColor = (NSColor *)[NSUnarchiver unarchiveObjectWithData:theData];
    return theColor;
}

@end


#pragma mark -
#pragma mark -

@implementation NSColor (CGColor)

+(NSColor *) colorWithCGColor:(CGColorRef) aColorRef
{
	NSColorSpace *colorSpace = [[NSColorSpace alloc] initWithCGColorSpace:CGColorGetColorSpace(aColorRef)];	
	NSColor *color = [NSColor colorWithColorSpace:colorSpace
									   components:CGColorGetComponents(aColorRef)
											count:CGColorGetNumberOfComponents(aColorRef)];
	[colorSpace release];
	return [color autorelease];
}

-(CGColorRef) CGColor
{
    const NSInteger numberOfComponents = [self numberOfComponents];
    CGFloat components[numberOfComponents];
    CGColorSpaceRef colorSpace = [[self colorSpace] CGColorSpace];
	
    [self getComponents:(CGFloat *)&components];
	
    return (CGColorRef)[(id)CGColorCreate(colorSpace, components) autorelease];
}

@end
 

chown33

Moderator
Staff member
Aug 9, 2009
10,706
8,346
A sea of green
Sometimes I see this on websites:
#if TARGET_OS_IPHONE (or similar)
do something
#else
do something else
#endif
Is this the best approach? To me it seems like every other line would duplicate itself until it's a gigantic mess of #if statements

If you find your code starting to do that, then it's time to refactor it.

Instead of line-by-line #if/#else, factor out a common (and presumably portable) class. Then in the implementation, you can put the #if/#else junk. All anyone else sees is a nice clean @interface.

And honestly, if the implementation of the portable class is too ugly as line-by-line #if/#else, factor that out, too. Example:
Code:
-someMethod
{
#if YOUR_CONSTANT_HERE
  blah;
  NSblah.blah;
  [blah blah:NSblahBlah];
#else
  blah;
  UIblah.fnord;
  [blurbish blah:UIblurbBlurb];
#endif
}

You can also make a private function that contains the conditional code, and the method then calls a series of those private functions to accomplish its overall task. The method stays nice and clean, and the purpose of the functions is clear because they have meaningful names (right?), but the gobbledy-gook of the specific implementation is put in one place, instead of being haphazardly interwoven like yak-hair into silk.

You can also use other tricks, such as conditional C macros. The name is common, e.g. MyPortableColor, but the expansion of the macro is target-dependent. That's more effective when the operations are simpler or the types are more primitive. It's usually not so nice when things get more complex. For an example of conditional macros that appear as if they're functions, look at NSAssert.
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
My question is about the last line:
Code:
return (CGColorRef)[(id)CGColorCreate(colorSpace, components) autorelease];
A corefoundation object is cast to an NSObject (?) and then autoreleased. Is this valid code or good practice?
I thought corefoundation objects could not be autoreleased.

Actually I don't think this is valid.

Some CoreFoundation types are toll-free bridged with their Cocoa counterparts. For example, a CFStringRef can be freely type cast to a NSString* and vice versa. So the following would be valid:
Code:
return (CFStringRef)
  [(id)CFStringCreateWithCStringNoCopy(
                kCFAllocatorDefault, 
                "Hello world",
                kCFStringEncodingASCII,
                kCFAllocatorNull) 
    autorelease];

But I can't find any documentation to say CGColorRef is toll-free bridged to any Cocoa type, so its bits are not guaranteed to be laid out compatibly with id. Although CGColorRef does "inherit" from CFTypeRef, perhaps this is a feature of CFTypeRef that I'm missing.

I also cannot find any documentation to say NSAutoreleasePool supports releasing CoreFoundation types, that is calling the CFRelease function instead of sending a release message.

Given all this I'm stumped on how to implement this with the correct object-ownership semantics. You can't add an instance variable to NSColor to hold the CGColorRef so you can release it later. I only think of implementing it as a create function so that the caller is responsible for freeing the resulting CGColorRef.

Code:
CGColorRef CGColorCreateFromNSColor(NSColor* anNSColor)
{
    const NSInteger numberOfComponents = [anNSColor numberOfComponents];
    CGFloat components[numberOfComponents];
    CGColorSpaceRef colorSpace = [[anNSColor colorSpace] CGColorSpace];
	
    [self getComponents:(CGFloat *)&components];
	
    return CGColorCreate(colorSpace, components);
}


Actually, I thought did occur to me. Wrap the pointer in a custom object like so.

Code:
@interface CGColorRefReleaser : NSObject
{
  CGColorRef* theColorRef;
}
- (id)initWithCGColorRef:(CGColorRef*)aColorRef;
@end

@implementation CGColorRefReleaser
- (id)initWithCGColorRef:(CGColorRef*)aColorRef
{
  self = [super init];
  if (self) {
    theColorRef = aColorRef;
  }
  return self;
}
- (void)dealloc
{
  CGColorRelease(theColorRef);
  [super dealloc];
}
@end

@implementation NSColor (CGColor)
-(CGColorRef) CGColor
{
    const NSInteger numberOfComponents = [self numberOfComponents];
    CGFloat components[numberOfComponents];
    CGColorSpaceRef colorSpace = [[self colorSpace] CGColorSpace];
	
    [self getComponents:(CGFloat *)&components];

    CGColorRef* colorRef = CGColorCreate(colorSpace, components);
    CGColorRefReleaser* releaser = 
      [[[CGColorRefReleaser alloc] initWithCGColorRef:colorRef] autorelease]
    return colorRef;
}
@end
 
Last edited:

MrFusion

macrumors 6502a
Original poster
Jun 8, 2005
613
0
West-Europe
Actually I don't think this is valid.
Neither do I, but I found it several times with google. Probably everyone copying from everyone.

But I can't find any documentation to say CGColorRef is toll-free bridged to any Cocoa type, so its bits are not guaranteed to be laid out compatibly with id. Although CGColorRef does "inherit" from CFTypeRef, perhaps this is a feature of CFTypeRef that I'm missing.

I also cannot find any documentation to say NSAutoreleasePool supports releasing CoreFoundation types, that is calling the CFRelease function instead of sending a release message.

That correspond to my (limited) understanding of corefoundation, so I agree with you.

Actually, I thought did occur to me. Wrap the pointer in a custom object like so.

Code:
@interface CGColorRefReleaser : NSObject
{
  CGColorRef* theColorRef;
}
- (id)initWithCGColorRef:(CGColorRef*)aColorRef;
@end

@implementation CGColorRefReleaser
- (id)initWithCGColorRef:(CGColorRef*)aColorRef
{
  self = [super init];
  if (self) {
    theColorRef = aColorRef;
  }
  return self;
}
- (void)dealloc
{
  CGColorRelease(theColorRef);
  [super dealloc];
}
@end

@implementation NSColor (CGColor)
-(CGColorRef) CGColor
{
    const NSInteger numberOfComponents = [self numberOfComponents];
    CGFloat components[numberOfComponents];
    CGColorSpaceRef colorSpace = [[self colorSpace] CGColorSpace];
	
    [self getComponents:(CGFloat *)&components];

    CGColorRef* colorRef = CGColorCreate(colorSpace, components);
    CGColorRefReleaser* releaser = 
      [[[CGColorRefReleaser alloc] initWithCGColorRef:colorRef] autorelease]
    return colorRef;
}
@end

Ah, so maybe this is how UIColor can have a CGColorRef method that is autoreleased (i.e. doesn't have to be released by the user). A from view hidden autoreleased NSObject subclass. But won't this immediately release the "releaser"? Nobody is holding on to it. The CGColorRef continues to be used, but not the "releaser" instance.
 

MrFusion

macrumors 6502a
Original poster
Jun 8, 2005
613
0
West-Europe
If you find your code starting to do that, then it's time to refactor it.

Instead of line-by-line #if/#else, factor out a common (and presumably portable) class. Then in the implementation, you can put the #if/#else junk. All anyone else sees is a nice clean @interface.

And honestly, if the implementation of the portable class is too ugly as line-by-line #if/#else, factor that out, too. Example:
Code:
-someMethod
{
#if YOUR_CONSTANT_HERE
  blah;
  NSblah.blah;
  [blah blah:NSblahBlah];
#else
  blah;
  UIblah.fnord;
  [blurbish blah:UIblurbBlurb];
#endif
}

You can also make a private function that contains the conditional code, and the method then calls a series of those private functions to accomplish its overall task. The method stays nice and clean, and the purpose of the functions is clear because they have meaningful names (right?), but the gobbledy-gook of the specific implementation is put in one place, instead of being haphazardly interwoven like yak-hair into silk.

You can also use other tricks, such as conditional C macros. The name is common, e.g. MyPortableColor, but the expansion of the macro is target-dependent. That's more effective when the operations are simpler or the types are more primitive. It's usually not so nice when things get more complex. For an example of conditional macros that appear as if they're functions, look at NSAssert.

The more I program, the longer my comments and method names become.

I like your suggestion of hiding the nasty details in private categories. Not so much the macros. Those can be tricky and difficult to debug, so I have read.

Thanks for the insight.
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
But won't this immediately release the "releaser"? Nobody is holding on to it. The CGColorRef continues to be used, but not the "releaser" instance.

Yes, if you turn garbage-collection on. This technique is an anti-pattern under garbage-collection.

If garbage-collection is off, then the "releaser" won't be released until the nearest NSAutoreleasePool is drained. At that point, the "releaser" will be released, its retain count will become zero, it's dealloc to be called, and CGColorRelease will be called on the CGColorRef. This is exactly the same semantics as the original code you posted, but more technically correct.

As per usual, if you wanted to keep the CGColorRef around, you need to call CFRetain and then later call CFRelease yourself. If you find yourself always wanting to do this, then save the overhead, and use the create function I posted instead.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,706
8,346
A sea of green
I like your suggestion of hiding the nasty details in private categories. Not so much the macros. Those can be tricky and difficult to debug, so I have read.

Yeah, hairy macros can be awful. I was mostly thinking of simple name substitution.

For example, if there's an NSSomething class and an UISomethingElse class, and the method and property names you use are the same, then write this:
Code:
#if TARGET_OS_IPHONE (or similar)
#define NSSomething UISomethingElse
#endif
and now you can use the NSSomething class name anywhere.

However, it may be best to signal that you're using a non-standard name:
Code:
#if TARGET_OS_IPHONE (or similar)
#define YourThing UISomethingElse
#else
#define YourThing NSSomething
#endif
 

jiminaus

macrumors 65816
Dec 16, 2010
1,449
1
Sydney
For example, if there's an NSSomething class and an UISomethingElse class, and the method and property names you use are the same, then write this:
Code:
#if TARGET_OS_IPHONE (or similar)
#define NSSomething UISomethingElse
#endif
and now you can use the NSSomething class name anywhere.

Nooo!!! :eek:

I can remember being a Novice C++ programmer and being stumped for a long time about why sometimes I would get an error about my MessageBox member function couldn't be found. It was because windows.h would #define MessageBox MessageBoxW. This was so programmers could just use MessageBox and just get the correct ANSI or Unicode version.

From that day forward #define's for anything other than conditional compilation have been by sworn enemy.
 

MrFusion

macrumors 6502a
Original poster
Jun 8, 2005
613
0
West-Europe
Yes, if you turn garbage-collection on. This technique is an anti-pattern under garbage-collection.

If garbage-collection is off, then the "releaser" won't be released until the nearest NSAutoreleasePool is drained. At that point, the "releaser" will be released, its retain count will become zero, it's dealloc to be called, and CGColorRelease will be called on the CGColorRef. This is exactly the same semantics as the original code you posted, but more technically correct.

As per usual, if you wanted to keep the CGColorRef around, you need to call CFRetain and then later call CFRelease yourself. If you find yourself always wanting to do this, then save the overhead, and use the create function I posted instead.

I don't use garbage collection. I prefer to keep my own tab on retain/release. But I also dislike getting a non-autoreleased object from a non-init method. I'll probably stick with the wrapper object. Although, this could maybe cause problems in lion with arc. Apparently, from the WWDC videos, retain/release is not needed anymore when using/compiling with arc. Well, I'll just have to wait for lion to try it out, I guess.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,706
8,346
A sea of green
Nooo!!! :eek:
...
From that day forward #define's for anything other than conditional compilation have been by sworn enemy.

Macros was the last of my suggestions in my original reply.

I advocate using the simplest and most robust solution that accomplishes a task. Sometimes that happens to be a macro. For example, look up NSAssert().

Trust me, anything can be abused. I can't tell you how many times I see "oversharing" of internal state with senseless proliferation of properties. Why? Because when they first learned it, there was a property for every instance variable, and they never really learned the why behind properties. Properties just became a crutch to avoid having to write retain/release when using ivars.
 

MrFusion

macrumors 6502a
Original poster
Jun 8, 2005
613
0
West-Europe
Trust me, anything can be abused. I can't tell you how many times I see "oversharing" of internal state with senseless proliferation of properties. Why? Because when they first learned it, there was a property for every instance variable, and they never really learned the why behind properties. Properties just became a crutch to avoid having to write retain/release when using ivars.

Oh, something tells me I might be guilty of this as well. :eek: Could you elaborate?
 

chown33

Moderator
Staff member
Aug 9, 2009
10,706
8,346
A sea of green
Oh, something tells me I might be guilty of this as well. :eek: Could you elaborate?

The basics are simple:
Properties are part of a class's public API. The only things that should appear in a public API are what's needed to interface that class with other classes. It's the Principle of Least Exposure, aka coupling.
http://en.wikipedia.org/wiki/Coupling_(computer_science)

I make the analogy with human public behavior. In polite company, we don't discuss bowel movements or publicly display our genitals. Yes, I realize it's a quaint anachronism on the internet, but a certain level of politeness remains an expected courtesy elsewhere. Think carefully about what you expose in public, or you might regret it later.

If there's no reason for a property to be public, and you're only using @synthesize to get auto-generated accessors for things like retain, copy, or atomic attributes, then put the properties in a private category or protocol, or in an anonymous class extension. Properties can be declared in any category or protocol.

You can also modify some attributes of properties in private categories or extensions. An example might be a public readonly property being redeclared as readwrite in private, so private code can use a setter method.

http://developer.apple.com/library/...ceptual/ObjectiveC/Chapters/ocProperties.html
http://developer.apple.com/library/...ceptual/ObjectiveC/Chapters/ocCategories.html

SIMPLE EXAMPLE CODE
Code:
#import <Foundation/Foundation.h>

@interface Fraction : NSObject
{
@private
   int numerator;
   int denominator;
}

@property  int numerator;
@property  int denominator;

@property (readonly)  double rational;

-(void) test;

@end


//@interface Fraction (PrivateMethods)  // classic named category
//-(void) private1;
//
//@end


@interface Fraction (  )  
  // empty ( ) means a class extension, effectively an anonymous category

@property (readonly)  NSString * frak;

-(void) reduce;

@end


@implementation Fraction;

// Override Inherited
-(NSString *) description
{  
   return [NSString stringWithFormat:@"%i/%i ", numerator, denominator];
}


// Public Properties
@synthesize numerator;
@synthesize denominator;

-(double) rational
{  return ((double) numerator) / denominator;  }


// Public Methods
-(void) test
{
   NSLog(@" frak: %@", self.frak );
   [self reduce];
   [self private1];  // has warning since no forward declaration
}


// Private
-(NSString *) frak
{  return [NSString stringWithFormat:@"%.15f ", self.rational];  }

-(void) private1
{  NSLog( @" private1 " );  }

-(void) reduce
{  NSLog( @" reduce " );  }

@end



int main(int arcgc, char *argv[])
{
   NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
   
   Fraction *frac = [[Fraction alloc] init];
   
   frac.numerator = 1;
   frac.denominator = 3;

   // These only use public API
   NSLog(@" Values of frac are:");
   NSLog(@" frac: %@", frac );
   NSLog(@" rational: %f", frac.rational );

   [frac test];

   // These compile only when main() is in same file as @implementation.
   // Otherwise these will cause compiler warnings or errors.
   NSLog(@" frak: %@", frac.frak );
   [frac private1];
   [frac reduce];

   [frac release];
   
   [pool drain];
   return 0;
}
The overall example is contrived, but it shows simple examples. I recommend trying it as-is in a single file (which is the way I wrote it), then breaking it out into separate files like you'd have in a typical project. Once broken out, it will become clear that main() can't reference the private properties or methods of the Fraction object. I thought it was important to see the in-scope case first, then break it out into separate scopes and see the result.

Note that the rational property depends on the values of numerator and denominator, and is not independent from them. Hence, it has no separate setter, i.e. is inherently readonly. Another example would be a Rectangle class with height and width properties, where area was a readonly property. The world is full of dependent properties like this.

One other thing about this example. The decision of whether to make something a simple method or a readonly property is something you should consider in the class design. I chose to define frak as a property, but it could also be declared as a method in the @interface:
Code:
-(NSString *) frak;
 

Sydde

macrumors 68030
Aug 17, 2009
2,552
7,050
IOKWARDI
I don't use garbage collection. I prefer to keep my own tab on retain/release.
If you are writing for both, it would seem that using GC on the Mac side would be counter-productive, inasmuch as it would require some different design between the two (since GC is not available for iOS), though ARC will probably change this dynamic in the future.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.