13

I've looked through a bunch of posts on this subject. Maybe I didn't run across "the one" and someone will point me in that direction. The question is simple and probably has a simple answer.

If you have two ivars, say, "public_ivar" and "private_ivar", where/how should you declare them so that what is public is public and what is private is not exposed in any way to anyone looking at the header file?

Same question in the case of "public_method" and "private_method".

I like clean header files (in other languages) that only expose the methods and ivars I want someone else to see. You should be able to publish your header file and not run into the danger of someone accessing something they are not supposed to. How do you do that in objective-C.

For example, let's say that I decide that I need to use an ivar to keep track of some data, a counter or somthing like that, between various class methods that all need access to this information. If that ivar is declared conventionally in the header under @interface its existence is publicly advertised and it is usable by anyone creating an instance of the class. The ideal scenario would be that this ivar would not be visible at all outside of the class implementation.

martin's
  • 3,853
  • 6
  • 32
  • 58

3 Answers3

17

You can declare instance variables or declared properties in a class extension. Since a class extension is declared in an implementation file (i.e., not a header file), they won’t be visible to someone inspecting the header file. For instance, in the header file:

@interface SomeClass : NSObject
@end

and in the implementation file:

@interface SomeClass ()
@property (nonatomic, assign) int privateInt;
@end

@implementation SomeClass

@synthesize privateInt;
…
@end

or

@interface SomeClass () {
    int privateInt;
}
@end

@implementation SomeClass
…
@end

Note that there’s nothing preventing access to private/class extension instance variables (or the accessor methods for properties declared in a class extension) during runtime. I’ve written a rather detailed post about this as an answer to another question on Stack Overflow: Does a private @property create an @private instance variable?

Edit: Instance variables in class extensions were presented in WWDC 2010 session 144.

Edit: "Using the Clang/LLVM 2.0 compiler, you can also declare properties and instance variables in a class extension."

http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocCategories.html#//apple_ref/doc/uid/TP30001163-CH20-SW1

Community
  • 1
  • 1
  • OK, I'll take a look at this. I am doing some work with Genetic Algorithms and Neural Networks. Classes end-up with piles of variables that are needed internally. Very few of these really need visibility at the header file level. Is your first example of the implementation file for iOS4+ (since you don't declare int privateInt; prior to the @property statement)? – martin's Apr 29 '11 at 00:17
  • @Martin I’m not sure when Objective-C 2.0, which allows automatic creation of backing instance variables, was made available to iOS. –  Apr 29 '11 at 00:19
  • @Martin [This page on Apple’s Web site](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtVersionsPlatforms.html%23//apple_ref/doc/uid/TP40008048-CH106-SW1) doesn’t list a particular iOS version. –  Apr 29 '11 at 00:21
  • Are you sure you can add ivars directly in extensions? The reference doesn't say anything about it. Can you post a reference, or is it just personal experience? – ughoavgfhw Apr 29 '11 at 00:23
  • @ugho Personal experience; I think this has been available since Clang 2.0. If you think that since this isn’t documented one shouldn’t rely on it, I kind of agree with you. But I have a strong feeling that this will make into Apple’s Objective-C reference because there’s no apparent harm in doing so and the modern runtime ABI supports non-fragile instance variables. –  Apr 29 '11 at 00:27
  • The reference explicitly says "No" to ivars in Categories, and is silent on the topic when it goes on to talk about Class Extensions separately. It seems safe to assume that, officially, that's a "No" to ivars in extensions. However, it's nice to hear that in actuality, it seems to be "Yes". @ughoavgfhw – jscs Apr 29 '11 at 01:04
  • @Josh @ugho I've filed rdar://problem/9356198 asking for clarification on the documentation and I've been told that ‘the right person’ has been CC’d on that report. –  Apr 29 '11 at 01:06
  • @Josh @ugho Instance variables in class extensions were presented in WWDC 2010 session 144 so I guess that makes it official. –  Apr 29 '11 at 01:31
  • Right on. I'll have to take a look at it. Thanks! – jscs Apr 29 '11 at 01:40
  • @Bavarious: What I was referring to is that apparently as of iOS 4.0+ you can --in the interface-- simply use @property rather than having to declare the ivar and also use @property. Did I get this wrong? Are category declarations different in this regard? Also, thanks for the research. I'll check out WWDC session 144 and see what I can learn. I'd be interested to learn what you might hear back from Apple on this. – martin's Apr 29 '11 at 01:48
  • @Bavarious: I took a lot at the long post you referred to. Great write-up. Thanks. – martin's Apr 29 '11 at 01:50
  • @Martin It is the same idea — but I can’t really tell _when_ this was introduced with regard to Xcode/iOS SDK versions. What I can say is that this is a feature of Objective-C 2.0 with the modern runtime. –  Apr 29 '11 at 01:51
  • @Josh @ugho Greg Parker says that instance variables in class extensions is official: [http://twitter.com/#!/gparker/status/63835867713773569](http://twitter.com/#!/gparker/status/63835867713773569). –  Apr 29 '11 at 05:39
  • @Bavarious: You might be interested to learn that the new "iOS Recipes" book (just got it) covers these techniques on page 242 "Leverage Modern Objective-C Class Design". Of the 20 or so iOS/Objective C books I have I think this is the only one that treats the subject. – martin's Apr 29 '11 at 19:56
  • Declaring a property is not the same as declaring an instance variable. Even if it's not recommended and properties are suggested instead, instance variables are declared in @implementation{ type var;} – Ekkstein Apr 01 '14 at 15:57
4

Use class extensions to add to a class in your implementation file. A class extension is basically an unnamed category with a few bonuses: properties declared in it can be synthesized and anything declared in it must be in the main implementation, so the compiler can check to make sure you didn't miss an implementation. You must put the class extension before your implementation. You can't add instance variables directly in a class extension, but you can add properties. When you synthesize accessors for properties which don't have corresponding instance variables, the new runtime (os x 10.5 and later and all versions of iOS, I believe) will create the instance variables automatically. This means you can't create your own accessors, however, unless you put the instance variable in your header. Private methods can be added to the class extension without restriction, but as Anomie noted, it is technically possible to use them if you know what they are called, and with class-dump, nothing is safe.

Example usage of a class extension:

@interface MyClass ()
@property (retain) id privateIvar;
@property (readwrite) id readonlyProperty; // bonus! class extensions can be used to make a property that is publicly readonly and privately readwrite
- (void)privateMethod;
@end
@implementation MyClass
@synthesize privateIvar; // the runtime will create the actual ivar, and we just access it through the property
- (void)privateMethod {
    ...
}
...

Another way of creating "instance variables" without putting them in the header or using a property is to use associative references, which add data to an object at runtime. They aren't technically the same as instance variables, and the syntax for them is more complex. Since they also require the new runtime, there are only two reasons you would ever really want to use them: you want to add an instance variable in a category (outside the scope of this question) or you need it to be really really private. An associative reference doesn't create any methods or add to the class's definition in the compiled code, so if you don't create wrappers for them it is impossible to find out about them without asking the object after you add the data. See the bottom of the page I linked for a complete usage example.

ughoavgfhw
  • 39,734
  • 6
  • 101
  • 123
  • So...if you need a customized setter or getter for an ivar "hidden" this way you can't write one without exposing the ivar in the header? – martin's Apr 29 '11 at 01:52
  • @Martin: As Bavarious said, and as discussed in the comments, ivars are now supported in class extensions, so you can declare it there instead of the header. You could also use associative references and write wrappers around them as accessors. – ughoavgfhw Apr 29 '11 at 01:55
2

You can use @private to specify that ivars are private. There is no way to make a method private, however. Even if the method is not listed in the header file, if someone knows the name and arguments they can call it.

Anomie
  • 92,546
  • 13
  • 126
  • 145
  • As I understand it @private doesn't really do anything if you have @property declarations (setters and getters). I don't even want the ivar or private member function to be in the header. On that assumption that nobody knows about them they should remain invisible (just trying to keep honest people honest, if you want to dig you can discover anything). I just think it is "dirty" to have all of this stuff in the header that is utterly irrelevant to other portions of an application. – martin's Apr 29 '11 at 00:03
  • 1
    @Martin: A property is not an ivar. As for hiding ivars in the header, I can think of two options: You could have the only ivar be a pointer to a struct that is declared elsewhere, or you could have all instances actually be private subclasses (along the lines of what Apple does with class clusters). – Anomie Apr 29 '11 at 00:52
  • Guilty of being lazy with language. I understand that @property simply creates the underlying setter and getter code. Does it do anything else? – martin's Apr 29 '11 at 01:44
  • @Martin: @property doesn't even create the underlying setter and getter. It tells the compiler that the `foo.property` syntax should use the getter and setter method, and allows @synthesize to work. And a little bookkeeping, so `class_getProperty` and other low-level methods can do their job. – Anomie Apr 29 '11 at 02:56