3

Is this possible ?

My colleague told me that naming a ivar with prefix underscore like what apple mostly does may cause some problem. He said that apple can declare an ivar in a class and doesn't let it be in the public header file. So if i accidentally name an ivar that get collide with that "secret ivar" which is not in the header file , this will cause a problem.

I don't know much about how frameworks works so I'm not sure about his explanation. I know apple only reserve the use of prefix underscore for only method name in there code guideline and many people do use prefix underscore when name an ivar and they said it's perfectly safe because if the name get collide the compiler would generate a error. I know this is true if the name of the ivar is in the header file. But what about that kind of "secret ivar" which is not in the public header file?

My colleague's another evidence for this is that if you dump an apple's application framework's header file and compare it with apple's public header file, you would find they don't match for many declaration of both methods and ivars.

I'm really confused by this question and hope someone can provide some professional answer and if you can provide some reliable reference about this question , that would be great help.

(I don't know whether I have explain my question clearly enough... Forgive my poor english...)

Jimmy
  • 1,094
  • 10
  • 22

3 Answers3

3

Your colleague might be referring to instance variables in class extensions. They do not show up in the (public) interface of the class. For instance,

// SomeClass.h -- this is a publicly distributed file
@interface SomeClass : NSObject
@end

// SomeClass.m -- this is known only to the framework developer
@interface SomeClass() {
    NSString *someIvar;
}
@end

@implementation SomeClass
- (id)init {
    self = [super init];
    if (self) someIvar = @"Default";
    return self;
}
@end

Note that someone that has access to the public headers only won’t know that SomeClass has an instance variable called someIvar by reading the header file that declares that class. Also, it won’t be possible to access that instance variable in the same way as accessing an instance variable declared in the public interface. In fact, there won’t be a name collision in case a developer subclasses SomeClass and declares an instance variable named someIvar. For instance, this is valid:

// SomeSubclass.h
@interface SomeSubclass : SomeClass {
    NSString *someIvar;
}
@end

In this case, any reference to someIvar in the implementation of SomeSubclass will refer to the instance variable declared in SomeSubclass only, and this instance variable is different from the one declared in the (private) class extension of SomeClass.

The compiler emits different symbol names for them:

  • _OBJC_IVAR_$_SomeClass.someIvar
  • _OBJC_IVAR_$_SomeSubclass.someIvar

and, upon instantiating an object of type SomeSubclass, both instance variables are located in different memory addresses.

While an inspection of the binary might show that SomeClass has an instance variable called someIvar even though it’s not listed in the public header file, there won’t be a name collision in subclasses.


That said, it might be possible that Apple have a set of public header files that they distribute with the SDK but use alternative, private header files that declare additional instance variables. I find that rather unlikely and there might be potential for binary incompatibility if that actually happens. I guess only Apple would be able to provide a definitive answer.

  • Extensions can't add ivars. The above code does not compile. @bbum has a short explanation of why extensions have the limitations they do here: http://www.friday.com/bbum/2009/09/11/class-extensions-explained/ – Rob Napier Apr 12 '11 at 02:46
  • @RobNapier That code does compile with Apple’s Clang/LLVM 2.0, so I guess it’s a recent addition. –  Apr 12 '11 at 02:54
  • @RobNapier @bbum comments in that post that the layout of instance variables must be known by the compiler in the 32-bit Mac OS X runtime. This could mean that this isn’t a problem in the modern runtime/ABI. –  Apr 12 '11 at 03:09
  • @Bavarious, I believe you and I have come to similar points. Below I demonstrate the same basic issues with @property. But I still can't get your code to compile. LLVM 2.0 says "Ivars may not be placed in class extension" (as I would expect). I switched to 64-bit Intel only (no 32-bit). clang-108.3. Have they changed it *that* recently (since preview 5 or so?) – Rob Napier Apr 12 '11 at 03:30
  • @RobNapier I’m using `Apple clang version 2.0 (tags/Apple/clang-137) (based on LLVM 2.9svn)`, the one that’s shipped with Xcode 4.0. –  Apr 12 '11 at 03:34
  • Found it. It's the option -fobjc-nonfragile-abi2, which was not on by default in earlier versions of Xcode. This also throws errors on my code below, noting the @property collision ("Property 'someIvar' is already implemented"). – Rob Napier Apr 12 '11 at 03:47
  • @RobNapie,@Bavarious,Why didn't i get the @property collision ("Property 'someIvar' is already implemented")error? I'm using the Xcode 4.0.1 and set the compiler in the project building setting -> C/C++ Compiler version to LLVM compiler 2.0. I used the same code that @RobNapie posted Did i set something wrong or missed somerthing? – Jimmy Apr 12 '11 at 05:18
  • @Jimmy Have you tried specifying `-fobjc-nonfragile-abi2` or `-fobjc-abi-version=3`? It should be the default for Mac OS X v10.6 64-bit targets. –  Apr 12 '11 at 07:14
  • @Bavarious where should i enter these two parameter?sorry for stupid question. I have used the code for both IOS targets and Mac os X Command line tool (10.6 64bit architechure).Both compile without any warning and error with LLVM2.0 – Jimmy Apr 12 '11 at 07:32
  • @Jimmy Project Navigator > Project > Build Settings > All (instead of Basic) > LLVM compiler 2.0 - Language > Other C Flags. –  Apr 12 '11 at 07:34
  • @Bavarious I only found LLVM-GCC 4.2-Language (in Mac command line tool target), and tried -fobjc-nonfragile-abi2 and -fobjc-abi-version=3 , the first one says "Unrecognized command line option",the second says "unknow objective-c abi flag".In iphone application , i found the one you mentioned and tried both option, they all worked fine and i still get no warning or error – Jimmy Apr 12 '11 at 07:44
  • @Jimmy In Xcode 4, this is set in the build pane as "Objective-C Non-Fragile ABI v2". You need to use the LLVM2.0 compiler, not the LLVM-GCC compiler (that's why I was not getting the warning originally). – Rob Napier Apr 12 '11 at 14:02
2

I'm going to invoke @bbum again hoping that he'll get the message and come and correct this answer.... But I'll do my best here.

In the old ABI, you could not hide your ivars. The compiler will catch the kinds of conflicts you're discussing in any case where they would matter. That said, I've found it's probably better to just avoid the problem by using some other naming convention. This way, when you find that there is already a _data in your superclass (as I have), you don't have to come up with some other random name, or accidentally access the superclass's private data when you didn't mean to. Despite my original dislike, Google's trailing underscore has grown on me since I used it in a large project.

The reason you couldn't hide your ivars (and if you did somehow, you wouldn't get name collisions anyway) is that ivars used to be just struct offsets. Hampster Emporium has a nice short post explaining this. At runtime, there is no _window, there is only offset 20. (Again, this is the old ABI I'm talking about).

This is not true for methods. They can collide. This is a bad thing when it happens. You don't get a warning. It really annoys me that Apple has private methods that do not have leading underscores on them. I have collided with them and the behavior is undefined. They reserved underscore; they should use it. (NSMutableArray had a private -pop method in a category that was implemented backwards of how I implemented my category with the same name. The resulting bugs in UINavigationController were entertaining to say the least.)

That digression to the side, with the new ABI, it is now possible to have "hidden" ivars. You just declare them as synthesized properties in a private class extension. But through the magic of the new ABI (ok, not magic, just pointers), the ivars are safe even if they collide. But the synthesized methods aren't safe from collision. Note how directly accessing the ivar below gives you the expected behavior, but using the property gives a surprising result. I haven't worked out how a developer should best avoid or even detect this kind of situation.

EDIT Based on my discussion with @Bavarious, I moved this code to the version of clang that comes with Xcode 4 (most of my boxes are still on Xcode 3). It complains with all the kinds of loud errors you'd hope for. Particularly you get "Property someIvar is already implemented." So that bodes very well for using @property the way you'd want to.

#import <Foundation/Foundation.h>

// SomeClass.h
@interface SomeClass : NSObject
@end

// SomeClass.m
@interface SomeClass ()
@property (copy) NSString *someIvar;
@end

@implementation SomeClass
@synthesize someIvar;
- (id)init {
    self = [super init];
    if (self) someIvar = @"SomeClass";
    return self;
}

- (void)print
{
    NSLog(@"Superclass=%@:%@", someIvar, [self someIvar]);
}
@end

// SubClass.h
@interface SubClass : SomeClass
{
    NSString *someIvar;
}
@property (copy) NSString *someIvar;
@end

// SubClass.m
@implementation SubClass
@synthesize someIvar;
- (id)init {
    self = [super init];
    if (self) someIvar = @"SubClass";
    return self;
}

- (void)print
{
    [super print];
    NSLog(@"Subclass=%@:%@", someIvar, [self someIvar]);
}

@end

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    SubClass *subclass = [[SubClass alloc] init];
    [subclass print];
    [pool drain];
    return 0;
}


Superclass=SomeClass:SubClass    <== Note the mismatch
Subclass=SubClass:SubClass
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

The compiler will let you know if there is a name clash with ivars, but not with method names.

From Apple's Developer's Guide:

Avoid the use of the underscore character as a prefix meaning private, especially in methods. Apple reserves the use of this convention. Use by third parties could result in name-space collisions; they might unwittingly override an existing private method with one of their own, with disastrous consequences. See “Private Methods” for suggestions on conventions to follow for private API.

FreeAsInBeer
  • 12,937
  • 5
  • 50
  • 82