Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Private ivar in @interface or @implementation

Tags:

Is there any reason to declare a private ivar in @interface instead of @implementation?

I see code like this all over the internet (including documentation provided by Apple):

Foo.h

@interface Foo : NSObject {
@private
    id _foo;
}
@end

Foo.m

@implementation Foo
// do something with _foo
@end

The header file defines the public interface of a class, whereas a private ivar is... well... private. So why not declare it like this?

Foo.h

@interface Foo : NSObject
@end

Foo.m

@implementation Foo {
@private
    id _foo;
}

// do something with _foo
@end
like image 786
Yevgeniy Avatar asked Jan 12 '12 19:01

Yevgeniy


People also ask

How do I declare iVar in Objective C?

For a private/protected variable, use iVar; for a public variable, use property. If you want to use the benifit of property attributes for a private variable, like retain, nonatomic etc., declare the property in the implementation file as a private property. For an iVar, you can use @private , @protected and @public .

What is Ivars Objective C?

Accessing an ivar through a getter/setting involves an Objective-C method call, which is much slower (at least 3-4 times) than a "normal" C function call and even a normal C function call would already be multiple times slower than accessing a struct member.

How do you access an Ivar inside a class?

For an iVar, you can use @private, @protectedand @public. But these attributes only influence the access of its subclass, and has nothing to do with the access ability of its instances. Go to herefor reference. usage Directly use an iVar inside the class, for example, photographer. But use self.for a property, for example, self.photographer.

What is the difference between Ivar and property in Java?

For a private/protected variable, use iVar; for a public variable, use property. If you want to use the benifit of property attributes for a private variable, like retain, nonatomic etc., declare the property in the implementation file as a private property.

What is the use of @synthesize in Ivar?

@synthesizeis only for property, not iVar. It will help the property generate an instance variable, the getter and setter accessors for the property and use the generated instance variable in the getter and setter accessors. For every property, complier will automatically synthesize it using _propertyName.


3 Answers

Declaring instance variables in the @implementation is a recent feature of Obj-C, this is why you see a lot of code with them in the @interface - there was no other choice.

If you are using a compiler which supports declaring instance variables in the implementation declaring them there is probably the best default - only put them in the interface if they need to be accessed by others.

Edit: Additional Info

Instance variables declared in the implementation are implicitly hidden (effectively private) and the visibility cannot be changed - @public, @protected and @private do not produce compiler errors (with the current Clang at least) but are ignored.

like image 131
CRD Avatar answered Nov 18 '22 16:11

CRD


You would favor @interface if you need compiler support targeting older systems or releases of Xcode.

If you are certain you will not need that backwards compatibility, I'd say it's best to place it in the @implementation.

  • I think @private is a good default.
  • It minimizes compile times, and reduces dependencies if you use it right.
  • You can reduce much of that noise at the top of your header. Many people will put #imports for their ivars, but they should use a forward declaration as default. So you can remove many #imports and many forward declarations from your header.
like image 41
justin Avatar answered Nov 18 '22 17:11

justin


The directives @public, @protected, and @private are not binding in objective-C, they are compiler hints about the accessibility of the variables. It DOES NOT RESTRICT YOU from accessing them.

example:

@interface Example : Object
{
@public
int x;
@private
int y;
}
...


...
id ex = [[Example alloc ] init];
ex->x = 10;
ex->y = -10;
printf(" x = %d , y = %d \n", ex->x , ex->y );
...

The gcc compiler spits out :

Main.m:56:1: warning: instance variable ‘y’ is @private; this will be a hard error in the future

Main.m:57:1: warning: instance variable ‘y’ is @private; this will be a hard error in the future

once for each "innapropriate" access to "private" member y, but compiles it anyway.

When run you get

x = 10 , y = -10

So it really is up to you NOT to write access code this way, but because objc is a superset of C, C syntax works just fine, and all classes are transparent.

You can set the compiler to treat these warnings as errors and bail -- but objective-C is not set up internally for this kind of strictness. The dynamic method dispatch would have to check for scope and permission for each call ( slooooowwwww... ) , so beyond a compile-time warning, the system expects the programmer to respect data member scoping.

There are several tricks to getting privacy of members in objective-C. One is to make sure you put the interface and implementations of your class in separate .h and .m files, respectively, and put the data members in the implementation file (the .m file). Then the files that import the headers do not have access to the data members, only the class itself. Then provide access methods (or not) in the header. You can implement setter/getter functions in the implementation file for diagnostic purposes if you want and they will be callable, but direct access to the data members will not be.

example:

@implementation Example2 :Object
{ 
 //nothing here
}
double hidden_d; // hey now this isn't seen by other files.
id classdata;    // neither is this.

-(id) classdata { return [classdata data]; } // public accessor
-(void) method2 { ... }
@end

// this is an "informal category" with no @interface section
// these methods are not "published" in the header but are valid for the class

@implementation Example2 (private)
-(void)set_hidden_d:(double)d { hidden_d = d; }

// You can only return by reference, not value, and the runtime sees (id) outside this file.
// You must cast to (double*) and de-reference it to use it outside of this file. 
-(id) hidden_d_ptr { return &hidden_d;}
@end

...
[Main.m]
...
ex2 = [[Example2 alloc] init];

double d = ex2->hidden_d; // error: 'struct Example2’ has no member named ‘hidden_d’
id data = ex2->classdata; // error: 'struct Example2’ has no member named ‘classdata’
id data = [ex2 classdata] // OK

[ex2 set_hidden_d : 6.28318 ]; // warning:'Example2' may not respond to '-set_hidden_d:'

double* dp = [ex2 hidden_d_ptr]; // (SO UGLY)  warning: initialization from incompatible pointer type
                                 // use (double*)cast -- <pointer-to-pointer conversion>  
double d = (*dp); // dereference pointer (also UGLY). 

...

The compiler will issue warnings for such blatant shenanigans, but will go ahead and trust that you know what you are doing (really?), and that you have your reasons (do you?). Seem like a lot of work? Error Prone? Yay Baby! Try refactoring your code first before resorting to magic C tricks and meatball surgery like this.

But there it is. Good luck.

like image 34
Chris Reid Avatar answered Nov 18 '22 16:11

Chris Reid