16

If you have a property in your public interface like the following

@interface MyClass : NSObject
@property(strong) NSString *myProp;
@end

And then synthesize it, in effect synthesizing the variable:

@implementation MyClass
@synthesize myProp = _myProp; // or just leave it at the default name..
@end

What is the visibility of the instance variable _myProp? That is, is this considered @public, @protected or @private? I'm guessing since MySubClass could inherit from MyClass then it would also get the properties (naturally), but would it also inherit the instance variable visibility?

What difference does it make if I put the property in a class extension? That would hide the property from subclasses, and I'm guessing the instance variable, too. Is this documented anywhere?

jbrennan
  • 11,943
  • 14
  • 73
  • 115

5 Answers5

29

A synthesized ivar is completely invisible to all code that cannot see the @synthesize line (which basically means anything outside of the .m file). It's not @protected, it's not @private, it's simply unknown. With a @private ivar, other code trying to access it will be told that it's private, but with a synthesized ivar, other code trying to access it will be told that the field simply doesn't exist.

As a thought experiment, try imagining a situation where the ivar acted like it was @protected. You make a subclass, and you muck about with the ivar there. Now you go back to the superclass and change @synthesize myProp to @synthesize myProp=foo. What happens in the subclass? When the compiler processes the subclass, it cannot see the @synthesize line, so it would have no idea that you just changed the name of the ivar. In fact, it cannot even tell if the property is backed by an ivar at all, or if it's implemented with custom-written accessor methods. I hope it's obvious why this means that the subclass cannot possibly access the ivar, and neither can any other class.

That said, I'm not quite sure what the compiler does if you write code in the same .m file that tries to access the ivar. I expect it will treat the ivar as @private (since the compiler can, in fact, see that the ivar exists).

Also, none of this has any bearing on the runtime methods. Other classes can still use the obj-c runtime methods to dynamically look up your class's ivar list and muck about with it.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
1

You can create a dynamic property and indicate it to the compiler that its instantiation would be at run time.

And then in your subclass write your own getter or synthesize the property.

@interface BaseClass : NSObject

@property (nonatomic, strong) NSString *ThisWillBeSynthesizedInRespectiveSubclasses;

@end

@implementation BaseClass

@dynamic ThisWillBeSynthesizedInRespectiveSubclasses;

@end

In Sub classes

@interface Subclass : BaseClass

@end

@implementation Subclass @synthesize ThisWillBeSynthesizedInRespectiveSubclasses = _ThisWillBeSynthesizedInRespectiveSubclasses;

@end

or you write your own setter / getter methods.

Hope this helps !

SRP-Achiever
  • 435
  • 4
  • 9
1

If it is declared in your interface it is virtually public when using the @property declarative. If you want to use @property declaratives and keep them property truly private, you should create a private category in your implementation.

MyClass.h

@interface MyClass : NSObject {
@private
    NSObject* foo;
}
@end

MyClass.m

#import "ClassWithPrivateProperty.h"

@interface MyClass ()
    @property (nonatomic,retain) NSObject* foo; 
@end

@implementation MyClass
@synthesize foo;
// class implementation...
@end
Chris Wagner
  • 20,773
  • 8
  • 74
  • 95
1

A synthesized variable acts as if declared @private:

@interface Garble : NSObject
@property (copy) NSString * s; 
@end
@implementation Garble
@synthesize s;
@end

@interface Bargle : Garble
@end

@implementation Bargle

- (void) useS {
    NSLog(@"%@", s);    // error: instance variable 's' is private
}

@end

I swear I've seen this in the docs, but I can't find it right now. Will update if I track it down.

jscs
  • 63,694
  • 13
  • 151
  • 195
0

Other classes have access to everything that they #include. In other words, to everything that is inside your header.

If something appears only in your implementation file, other classes (including subclasses) don't know it exists. A synthesized property is like that. Other classes know only about the property (a property means a getter and a setter method) but they don't know anything about the inner implementation of its methods.

Note, that the access specifiers (public/private/protected) in obj-c are only a hint to the compiler that even if something appears in the header file, it can't be accessed. The runtime does not check it in any way.

What happens if you put it into a class extension? Note that a property is a set of two methods. You just hide the methods from every class which includes your class main header but not the class extension header.

We use this for example to declare a property as readonly and in class continuation we declare it as readwrite. Then, we can use the setter only from inside of the class.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • Your first three paragraphs are somewhat inaccurate; the synthesized variable still can't be accessed even if it's defined in the same file (see my post). Second, the [scope directives](http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocDefiningClasses.html#//apple_ref/doc/uid/TP30001163-CH12-TPXREF127) are more than just hints; a compiler _error_ will result from trying to access an `@private` ivar. You're right that it's possible to get around that with runtime functions. – jscs Dec 15 '11 at 07:34
  • You are right, there can be two implementations in one file and then in behaves as @private. Anyway, the first hint why it can't be accessed is because it's not included. – Sulthan Dec 15 '11 at 08:54