I've been trying to find a workaround to declare #protected properties in Objective-C so only subclasses in the hierarchy can access them (read only, not write).
I read that there is no documented way of doing this so I thought of this workaround and I wanted to ask StackOverflow's opinion about it.
Every custom class at the top of the hierarchy contains three classes, one implementation and two interfaces.
Let's name them:
ClassA.h
ClassA_protected.h
ClassA.m
Then any subclass of this ClassA would be as usual:
ClassB.h
ClassB.m
First I created the interface ClassA.h where I declare a protected int variable so any subclass of ClassA can have access to it:
#interface ClassA : NSObject{
#protected
int _myProtectedInt;
}
#end
Next step is the workaround I was talking about. However, once you read it you will see that it is quite straight forward. I declared a second interface called ClassA_protected.h which actually works as an extension of ClassA.h and allows us to tag the property as readonly:
#import "ClassA.h"
#interface ClassA ()
#property (nonatomic , readonly) int myProtectedInt;
#end
Last step of preparing the protected hierarchy is to declare its implementation in ClassA.m where we only synthesize our property:
#import "ClassA_protected.h"
#implementation ClassA
#synthesize myProtectedInt = _ myProtectedInt;
#end
This way, every class that needs to be a subclass of ClassA.h, will import ClassA_protected.h instead. So a child like, for example ClassB.h, would be as follows:
#import "ClassA_protected.h"
#interface ClassB : ClassA
#end
And an example of accessing this property from ClassB.m's implementation:
#implementation ClassB
-(void) method {
//edit protected variable
_myProtectedInt= 1;
//normal access
self.muProtectedInt;
}
#end
Sure, that works fine. Apple uses the same approach for example in the UIGestureRecognizer class. Subclasses have to import the additional UIGestureRecognizerSubclass.h file and override the methods that are declared in that file.
For simple "properties" just use ivar instead. That's as good as properties for all practical purposes.
Moreover, the default is already protected.
If you ask for opinion, this is mine: If one decides to mutate your
_myProtectedInt
he will probably succed anyway, because it's definitely possible with Objective-C runtime. Except this, your solution is quite OK.
Import the protected header in the implementation only. e.g.
ClassB.h
#import "ClassA.h"
#interface ClassB : ClassA
#end
ClassB.m
#import "ClassA_protected.h"
#implementation ClassB
#end
And in a framework the protected header should be marked project so it is not included in the public headers of the framework. Apple usually use the suffix _Internal.h for their protected methods.
For init or overriding a lazy loaded get property you would need direct access to the #proteced ivar, however for your use it would be better to redeclare the property as readwrite instead then you can take advantage of any features of the setter, atomicity for example.
Related
Following this blog post, I saw a way to solve the problem I was facing.
My problem, like his, was that I have a class that has a property which must be inherited and accessed in its subclass:
#interface A : NSObject
#property (nonatomic, readonly) NSUInteger prop;
#end
#implementation A
// Don't need to synthesize nowadays
#end
#interface B : A
// No new properties
#end
#implementation B
- (void)establishValueForProp
{
_prop = 1; // PROBLEM !!!
}
#end
The solution was this:
#interface A : NSObject {
#protected
NSUInteger _prop;
}
#property (nonatomic, readonly) NSUInteger prop;
#end
What I'm wondering is if there is another way to declare properties as protected?
The way I typically do this is to create a second header, called e.g. "ClassName_ForSubclasses.h". Inside that header file, add a class extension with the properties and methods you want subclasses to be able to call and override. Then, subclasses can import that header, while other users of the class(es) don't see it.
Another common way of doing it, is by creating a separate .h file (ASubclass.h, for example) and adding something like this:
#interface A (Protected)
#property (nonatomic, readonly) NSUInteger prop;
#end
Subclassers can then import this .h and will have access to this method. Note that since categories can't add properties, you'll have to redefine this property in A's private interface (class extension). The category will then provide public access to this property. Apple takes this exact approach with UIGestureRecognizer.h and UIGestureRecognizerSubclass.h (where UIGestureRecognizer.h exposes more methods, intended for subclassers to override).
The concept of protected (or private, for that matter) methods doesn't really exist, and this is only a way to somewhat achieve similar functionality.
Personally, I just create a category in the (only) .h file of the class that exposes an otherwise public property. This way it's separated from the main public interface, and since it's possible to get to private properties anyway, I think this approach is good enough.
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.
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.
OK basically I have a class in an iPhone app where I want it to some read only propertys. Meaning that the owning class can read and write the property, but other objects can only read it. I try the "readonly" option when I declare the property, but then my class can't even write it. What use is that?
Let's assume you wanted to create a property called foo, an int, in your class YourClass.
Do this in your interface (.h) file:
#property(readonly) int foo;
Then in your implementation (.m) file, set up a class extension where you can re-define your property.
#interface YourClass()
#property(readwrite) int foo;
#end
This results in the property being readonly publicly, but readwrite privately.
Then, of course, you synthesize foo in your implementation that follows.
#synthesize foo;
If it's not too inconvenient, just use the ivar or "backing" variable in your class to modify the value. Like this:
In your .h file:
#interface ClassName
#property (readonly,nonatomic) NSInteger readOnlyValue;
#end
In your .m file:
#implementation ClassName
#synthesize readOnlyValue = _readOnlyValue;
_readOnlyValue = 42;
#end
While you could go the route of direct iVar access as described in other answers, a better solution is typically to use class extensions. They were designed exactly to solve this problem and, by using them, you can easily refactor your code later to expose the readwrite definition of the #property to other classes in your app without exposing it to all classes.
I wrote up a detailed explanation a while ago.
You can implement your own setter in the .m class or synteshize as:
foo = _foo; so you can call _foo (private variable) internally
On further reflection, the easiest way to achieve this is to add a normal property in a class extension, then declare just the getter in the header. E.g.
Interface:
#interface MyClass: NSObject
- (NSString *)someString;
#end
Implementation:
#interface MyClass ()
#property (nonatomic, retain) NSString *someString;
#end
#implementation MyClass
#synthesize someString;
#end
You'll be able to get and set, using dot notation or otherwise, and directly access the someString instance variable within the class, and everyone that has sight of the interface will be able to get, using dot notation or otherwise.
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.