Public iVar; readonly from other classes - objective-c

What is the best way to make an iVar writeable by its owning class, but readonly by other classes?
Is there more than one way?
The only way I can think of is to set a #property (readonly) for the iVar, but not use dot notation inside the owning class (which I'm guessing wouldn't work with readonly), and in the owning class just reference it directly, without dot notation, then in other class, reference it with dot notation.
But I'm curious if there are other methods.

You can make a property read/write just for the implementation. You do this using a class extension, or anonymous category, in your implementation. Easier to demonstrate so, for example, in your .h:
#interface MyClass
#property (readonly) NSInteger age;
...
#end
and in your .m:
#interface MyClass () // class extension/anonymous category for private methods & properties
// implementation of these go in main #implementation
#property (readwrite) NSInteger age;
...
#end
#implementation MyClass
#synthesize age;
...
#end
Now you can use the property within your class read/write and from outside as read-only.
You do the same for private methods.
[Note: This is Objective-C so the above isn't bullet proof - there are ways around it to call the properties setter - but the compiler will flag errors which assign to the property from outside the class.]
After comments:
[Note 2: an anonymous category/class extension is very similar to a named category. The former must be implemented in the main implementation, the latter may be implemented in an #implementation classname (categoryname) or in the main implementation. Furthermore only the main interface, the interface for a class extension/anonymous category, and the main implementation may declare instance variables; interfaces and implementations for named categories may not. Properties may be declared in both anonymous and named categories, however they may only be synthesized in the main implementation and not in named category implementations. This note is correct at press time, some of these features have changed as Objective-C/Xcode develops.]

Use the private specifier for ivars and a class extension to define the accessor - that way the ivar is private to the class.
// FooClass.h
#interface FooClass : NSObject {
#private int boo;
}
#end
// FooClass.m
#interface FooClass () {
}
#property (nonatomic,assign) int boo;
#end
#implementation FooClass
#synthesize boo;
// ....
#end

Related

Quick inquiry about ivars scope [duplicate]

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?
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.
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
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.
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 !
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.

Inheriting accessors in Objective-C

this is my first post; this site has been an invaluable resource.
I'm fairly new to objective-c so please bear with.
So I have a base class with a few properties which I want "private" so I made them readonly. To be clear, I don't want them mutable externally, but I DO wan't to use the 'set' accessor within this class. So...
// .h file
#interface Vehicle
#property (nonatomic, readonly) int speed;
#end
Also I repeated the property declaration within a category interface block to make the accessors writable in this class
// .m file
//Private properties and methods
#interface Vehicle()
#property (nonatomic, readwrite) int speed;
#end
#implementation
#synthesize speed = _speed;
- (void) someMethod {
[self setSpeed:10]; // Works fine
}
#end
But now if I inherit this class the derived class no longer has the set accessor method (setSpeed in my case). Do I need to synthesize again? Seems like that would defeat the purpose of inheritence. I know i can modify the instance variable directly (_speed = 10;) but would rather not. I'm sure there's something wrong with my understanding. Thanks!
// Example
#interface Ship : Vehicle
#end
#implementation
- (void) someOtherMethod {
[self setSpeed: 2]; // DOES NOT WORK, would like it to
}
#end
But now if I inherit this class the derived class no longer has the set accessor method (setSpeed in my case).
Actually, it does have the set accessor, it's just that the compiler doesn't know about it. You have a choice:
put the class extension (the #interface Vehicle() .... #end bit in a separate header file that gets imported into the .m for Vehicle and its subclasses (or use a category)
redeclare the read/write property in a class extension for the subclass. To avoid a warning, use #dynamic speed in the subclass's implementation.
Since there is no such thing as 'protected' methods, you need to create a private shared header where your anonymous category goes. Then both your original implementation and your derived classes include this header to get access to this 'private' stuff.

What's preferable Categories or Private Attributes in the Interface

What's preferable and a clean solution for attributes in a class. Use categories (anonymous) or #private annotation in the interface definition (.h file).
The attributes should not be inherited by other classes.
BR,
mybecks
Categories and #private are two different things. Categories extend the classes where you can add methods to a class and #private is where other classes can't access your attributes directly and will have to use your accessors. If you want ivars that subclasses can't access, use #private in your interface.
#interface MyClass : NSObject
{
#private
int num; //private attribute where subclass can't access
}
First up read this to understand the #private modifier What does "#private" mean in Objective-C?
And then to answer your questions.
An anonymous category is called a class extension and this is what I use to keep my API's clean and to not publicly announce my ivars.
e.g
// .h
#interface MyClass : NSObject
#end
// .m
#interface MyClass ()
#property (nonatomic, strong) NSDate *someDateImWorkingWith;
#end
#implementation MyClass
#synthesize someDateImWorkingWith = _someDateImWorkingWith;
#end
As a result I have the NSDate *someDateImWorkingWith to use within my class but no one importing my header will even know it exists without doing a little bit of digging. This works well for me your milage may vary.

What is the visibility of #synthesized instance variables?

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?
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.
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
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.
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 !
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.

Should I always use accessors for instance variables in Objective-C?

If I have a class with some IBOutlets, it seems kind of silly to create accessors for these.
But then I feel like I'm breaking OO practices by not always going through the accessors for instance variables.
I also feel the same way about some instance variables that should not be public, I'd rather not expose the inner workings of some classes. I can make the actual ivars private but the #property shorthand doesn't seem to be able to specify visibility. This leads me to not create accessors and just access the ivars directly. I'm not sure if this is frowned upon though. Is it?
What's the community's thoughts on this admittedly newbie question? (Please ignore dot syntax)
I'm not sure about accessing instance variables directly, I think one shouldn't, but for some variables it just doesn't make sense to use accessors. Like for the IBOutlets you mentioned.
I can only help you out with private accessors. Starting with Objective-C 2.0 you can declare extensions.
Class extensions are like “anonymous”
categories, except that the methods
they declare must be implemented in
the main #implementation block for the
corresponding class.
Just put this extension into a separate header file and you'll have private accessors that aren't visible in the header.
Public/Private
You can declare your iVars as in the #interface file to be readonly, but then re-declare them in a category so that your class can change them. Here's a quick intro to Categories.
An example:
//MyClass.h
#interface MyClass : NSObject {
NSString *name;
}
#property (readonly) NSString *name;
#end
And in the implementation file you can redeclare this:
//MyClass.m
#interface MyClass () //declare the class extension
#property (readwrite, copy) NSString *name; //redeclare the property
#end
#implementation MyClass
#synthesize name;
#end
Now, the name property is readonly external to the class, but can be changed by the class through property syntax or setter/getter syntax.
Really private iVars
If you want to keep iVars really private and only access them directly without going through #property syntax you can declare them with the #private keyword. But then you say "Ah, but they can always get the value outside the class using KVC methods such as setValueForKey:" In which case take a look at the NSKeyValueCoding protocol class method + (BOOL)accessInstanceVariablesDirectly which stops this.
IBOutlets as properties
The recommended way is to use #property and #synthesize. For Mac OS X, you can just declare them as readonly properties. For example:
//MyClass.h
#interface MyClass : NSObject {
NSView *myView;
}
#property (readonly) IBOutlet NSView *myView;
#end
//MyClass.m
#implementation MyClass
#synthesize myView;
#end