How are declared private ivars different from synthesized ivars?
I know that the modern Objective-C runtime can synthesize ivars. I thought that synthesized ivars behaved exactly like declared ivars that are marked @private
, but they don't.
As a result, come code compiles only under the modern runtime that I expected would work on either. For example, a superclass:
@interface A : NSObject {
#if !__OBJC2__
@private
NSString *_c;
#endif
}
@property (nonatomic, copy) NSString *d;
@end
@implementation A
@synthesize d=_c;
- (void)dealloc {
[_c release];
[super dealloc];
}
@end
and a subclass:
@interface B : A {
#if !__OBJC2__
@private
NSString *_c;
#endif
}
@property (nonatomic, copy) NSString *e;
@end
@implementation B
@synthesize e=_c;
- (void)dealloc {
[_c release];
[sup开发者_运维技巧er dealloc];
}
@end
A subclass can't have a declared ivar with the same name as one of its superclass's declared ivars, even if the superclass's ivar is private. This seems to me like a violation of the meaning of @private
, since the subclass is affected by the superclass's choice of something private.
What I'm more concerned about, however, is how should I think about synthesized ivars. I thought they acted like declared private ivars, but without the fragile base class problem. Maybe that's right, and I just don't understand the fragile base class problem. Why does the above code compile only in the modern runtime? Does the fragile base class problem exist when all superclass instance variables are private?
Synthesized ivars are private. And what you've seen is the compiler working correctly.
Ignore the code inside the !__OBJ2__
conditionals. I'll only be looking at the synthesized ivars case.
This is what your code has:
A::_c
is a synthesized ivar and is only accessible within the implementation ofA
.B::_c
is a synthesized ivar and is only accessible within the implementation ofB
.
They are not the same variable. They do not collide and do not store the same value.
Situation where a problem can still occur...
If you try to put A
and B
's implementation in the same file, the compiler will now be able to see the declaration of A::_c
while it is compiling B and will prevent you accessing _c
in the @synthesize e=_c;
line in B
.
Why would it do this? Didn't I just say that A::_c
and B::_c
were separate, unrelated variables?
Having both implementations in the same file creates a situation where _c
is not an undeclared identifier when the compiler reaches @synthesize e=_c;
, so the compiler will not try to create a new synthesizes ivar for B
, instead it tries to access A::_c
and fails (since A::_c
is private).
In short, you can't [have private ivars].
An alternative is to declare a class in your implementation file that contains all the state, then treat it like a struct.
.m:
@interface PrivateGoo:NSObject
{... ivars ...}
... props
@end
@implementation PrivateGoo
... @synth or not
Then, in the .h, declare an ivar like id privateGoop;
. If speed of lookup is a terrible concern, use ((PrivateGoo*)privateGoop)->iVar;
in your .m file. Be careful about memory management if you do.
Note that this has a couple of advantages, even if it is seemingly odd at first glance. Namely, it makes refactoring out PrivateGoo trivial; if your data encapsulation suddenly finds a need to have business logic or be made more universally available, doing so is trivial. With GC, the compiler will lay down the layout information for PrivateGoo and will manage all of the memory entirely automatically. Using a structure requires a slight bit of hoop jumping to make work right -- correct allocation patterns, etc.
Sorry -- didn't actually answer the question.
Synthesized ivars work exactly like regular ivars except that you don't have to declare 'em in the header. There isn't anything particular private about them other than that. Note that synthesized ivars are not required to avoid the fragile base class problem.
The @private
, @protected
and @public
directives exist for the benefit of the compiler; to give it a hook to warn (or error) when you access something that is declared as inaccessible. There is still OBjective-C's relatively flat namespace to content with and that is what prevents you from having an iVar of the same name in a subclass.
The fragile base class problem is orthogonal to properties and synthesized ivars. Fragile base classses were addressed in the modern Objective-C 2.0 ABI on the iPhone OS and 64 bit Mac OS X. 32 bit Mac OS X preserves the legacy behavior for binary compatibility reasons.
精彩评论