I'm trying to add an instance variable and a property to an existing class. I want to do this to extend the base class of an open source library, without modifying the source code (for easier code management).
The documentation says
Unlike regular categories, a class extension can add its own
properties and instance variables to a class. If you declare a
property in a class extension [...] the compiler will automatically
synthesize the relevant accessor methods, as well as an instance
variable, inside the primary class implementation.
I tried to do this in a test app. I have an empty class ClassA:
ClassA.h
#import <Foundation/Foundation.h>
#interface ClassA : NSObject
#end
ClassA.m
#import "ClassA.h"
#implementation ClassA
#end
Then I added an extension:
ClassA+Ext.h
#import "ClassA.h"
#interface ClassA ()
#property (nonatomic) NSString *name; // <-- this is my new property
#end
Finally in my AppDelegate I simply init a ClassA and try to set and then log the name.
#import "AppDelegate.h"
#import "ClassA.h"
#import "ClassA+Ext.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
ClassA *a = [[ClassA alloc] init];
a.name = #"test";
NSLog(#"Name: %#", a.name);
}
#end
This compiles and runs, but I get this error on the console:
2013-05-10 00:58:08.598 Test[53161:303] -[ClassA setName:]: unrecognized selector
sent to instance 0x108a1a630
2013-05-10 00:58:08.600 Test[53161:303] -[ClassA setName:]: unrecognized selector
sent to instance 0x108a1a630
The name is not logged. And if I set a breakpoint and inspect the ClassA instance, it doesn't have a name ivar. What am I doing wrong? Isn't this actually possible to do?
Your understanding is incorrect. You added a property through a class extension. This property should only be able to be used inside your class. It should not be used as a true "property" for your class in which you can change it through external instantiation. Think of it as a pseudo private instance variable.
I believe what you want to do is simply subclass your Class A.
Another viable solution proposed was:
"You can write a category to wrap it into -[setAssociatedObject:forKey:]" when using objc_setAssociatedObject --> by #Artur
I think class extensions are meant to be put in the class.m files, usually above the #implementation of the class.
Related
In one class I have the following
AlertEditorContainerViewController.m
#import "AlertEditorContainerViewController.h"
#interface AlertEditorContainerViewController ()
-(void)swapViewControllers;
#end
#implementation AlertEditorContainerViewController
#synthesize currentSegueIdentifier;
#synthesize segIndex;
- (void)swapViewControllers
{
self.currentSegueIdentifier = ([self.currentSegueIdentifier isEqual: SegueIdentifierFirst]) ? SegueIdentifierSecond : SegueIdentifierFirst;
[self performSegueWithIdentifier:self.currentSegueIdentifier sender:nil];
}
#end
The in another class I try to call it
AlertEditorViewController.h
#interface AlertEditorViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
{
AlertEditorContainerViewController *containerViewController;
}
AlertEditorViewController.h
#import "AlertEditorViewController.h"
#implementation AlertEditorViewController
- (IBAction)segmentSwitchValueChanged:(id)sender
{
[containerViewController swapViewControllers];
}
#end
This gives the error "No visible #interface for AlertEditorContainerViewController declares the selector swapViewControllers'
I have looked up all of the other similar queries and they all seem to point at typos etc which I can't find in my code.
Declare -(void)swapViewControllers in your AlertEditorContainerViewController.h file, not your .m file.
For information on this problem, you'll want to check out this Stack Overflow question, but in short, by declaring the method within the #interface block located in your .m file, you're effectively making it a private method (inaccessible by other implementation files). From the link:
The interface section in the implementation file allows you to declare variables, properties, and methods that are private, meaning that they won't be seen by other classes.
The compiler knows it's there within the scope of that particular file, but other files can't access or even see that method as being defined.
There is a class that looks like this (I'm omitting the imports for brevity):
Base.h:
#interface Base : NSObject
#property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
#end
Base.m:
#implementation Base
- (id)initWithSomething:(NSString *)something {
self = [super init];
if (self) _something = something;
return self;
}
#end
As you see, the 'something' property is readonly. Now I want to create a subclass that overrides that property to be writable as well:
Sub.h:
#interface Sub : Base
#property (strong) NSString *something;
#end
Sub.m:
#implementation Sub
#end
And the code:
main.c:
int main(int argc, const char * argv[]) {
#autoreleasepool {
Sub *o = [Sub new];
o.something = #"foo";
NSLog(#"%#", o.something);
}
return 0;
}
This code results in:
2013-09-07 13:58:36.970 ClilTest[3094:303] *** Terminating app due to uncaught
exception 'NSInvalidArgumentException', reason: '-[Sub setSomething:]: unrecognized
selector sent to instance 0x100109ff0'
Why is that? Why doesn't it find the setSelector?
When I do this in the subclass instead:
Sub.m:
#implementation Sub
#synthesize something = _something;
#end
it all works. Does this mean the subclass' property is not synthesized by default even though it is defined as #property in the #interface? Does the compile somehow 'see' the automatically generated getter from Base and doesn't generate the setter? And why, I think the setter should be generated as it doesn't exist yet. I'm using Xcode 4.6.2 and the project is a Cli Tool (type Foundation), but the same happens in my actual project which is an iPhone app.
Background: I have a heavy object (instance of Base) that requires a Bluetooth connection to some equipment and I am supposed to create a view controller for some functionality. For easy testing I don't want to be connected to BT (actually, I would need a physical device and test the code on it), I would like to be able to test it in the simulator.
What I came up with is that I simply create a subclass (Sub) that stubs a few methods / properties and use it instead, and when the code is ready I just remove the code for the subclass, replace its instance with the correct one, test in with a device, commit and push. It actually works fine, except for the weird thing with #property above.
Could somebody tell me what is going on with property overriding?
For a readonly property, only a getter method is synthesized, but no setter method.
And when compiling the subclass, the compiler does not know how the property is realized
in the base class (it could be a custom getter instead of a backing instance variable).
So it cannot just create a setter method in the subclass.
If you want to have write access to the same instance variable from the subclass,
you have to declare it as #protected in the base class
(so that it is accessible in the subclass), re-declare the property
as read-write in the subclass, and provide a setter method:
Base.h:
#interface Base : NSObject {
#protected
NSString *_something;
}
#property (strong, readonly) NSString *something;
- (id)initWithSomething:(NSString *)something;
#end
Sub.h:
#interface Sub : Base
#property (strong, readwrite) NSString *something;
#end
Sub.m:
#implementation Sub
-(void)setSomething:(NSString *)something
{
_something = something;
}
#end
Your solution
#synthesize something = _something;
generates getter and setter method in the subclass, using a separate instance
variable _something in the subclass (which is different
from _something in the base class).
This works as well, you just should be aware that self.something refers to
different instance variables in the base class and in the subclass. To make that
more obvious, you could use a different instance variable in the subclass:
#synthesize something = _somethingElse;
The given answer works perfectly fine. This is an alternative answer, that apparently Apple likes a bit more.
You can define a private extension of your class, a Base+Protected.h file, which needs to be included in Base.m and Sub.m.
Then, in this new file, you redefine the property as readwrite.
#interface Base ()
#property (strong, readwrite) NSString *something;
#end
This alternative allows you to use the accessor self.something rathern than the ivar _something.
Note: you still need to keep the definition of something in your Base.h as is.
I guess that the backing variables are the same when the property is not synthesized in the subclass. So at runtime the programm tries to call the setSomething in the superclass. But since it doesnt exist there an Exception is thrown.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Protected methods in objective-c
The way to declare private properties is simple.
You declare that in extension that's declared in .m files.
Say I want to declare protected properties and access it from the class and subclass.
This is what I tried:
//
// BGGoogleMap+protected.h
//
//
#import "BGGoogleMap.h"
#interface BGGoogleMap ()
#property (strong,nonatomic) NSString * protectedHello;
#end
That one is compile. Then I added:
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap ()
-(NSString *) protectedHello
{
return _
}
#end
Problem starts. I can't implement class extension outside the original .m files it seems. Xcode will demand something inside that bracket.
If I do
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap (protected)
-(NSString *) protectedHello
{
return _
}
#end
I cannot access the ivar of _protectedHello declared in BGGoogleMap+protected.h
Of course I can use regular category rather than extension, but that means I can't have protected properties.
So what should I do?
The Objective-C Programming Language says this:
Class extensions are like anonymous categories, except that the methods they declare must be implemented in the main #implementation block for the corresponding class.
So you could just implement your class extension's methods in the class's main #implementation. That is the simplest solution.
A more complicated solution is to declare your “protected” messages and properties in a category, and declare any instance variables for that category in a class extension. Here's the category:
BGGoogleMap+protected.h
#import "BGGoogleMap.h"
#interface BGGoogleMap (protected)
#property (nonatomic) NSString * protectedHello;
#end
Since a category cannot add an instance variable to hold protectedHello, we need a class extension also:
`BGGoogleMap_protectedInstanceVariables.h'
#import "BGGoogleMap.h"
#interface BGGoogleMap () {
NSString *_protectedHello;
}
#end
We need to include the class extension in the main #implementation file so that the compiler will emit the instance variable in the .o file:
BGGoogleMap.m
#import "BGGoogleMap.h"
#import "BGGoogleMap_protectedInstanceVariables.h"
#implementation BGGoogleMap
...
And we need to include the class extension in the category #implementation file so that the category methods can access the instance variables. Since we declared the protectedHello property in a category, the compiler will not synthesize the setter and getter method. We have to write them by hand:
BGGoogleMap+protected.m
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap (protected)
- (void)setProtectedHello:(NSString *)newValue {
_protectedHello = newValue; // assuming ARC
}
- (NSString *)protectedHello {
return _protectedHello;
}
#end
Subclasses should import BGGoogleMap+protected.h to be able to use the protectedHello property. They should not import BGGoogleMap_protectedInstanceVariables.h because the instance variables should be treated as private to the base class. If you ship a static library without source code, and you want users of the library to be able to subclass BGGoogleMap, ship the BGGoogleMap.h and BGGoogleMap+protected.h headers, but don't ship the BGGoogleMap_protectedInstanceVariables.h header.
I wish I could tell you otherwise but you just can't. See this question for more information: Protected methods in Objective-C.
I am not sure, what you want to do? Something Hacking or Cracking of Data Abstraction out of OOPS concept?
Extensions are used to add properties. You have successfully added private property as in
#import "BGGoogleMap.h"
#interface BGGoogleMap ()
#property (strong,nonatomic) NSString * protectedHello;
#end
What are you doing in this ?
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap ()
-(NSString *) protectedHello
{
return _
}
#end
You have extended a class, now you are again implementing same class !!! Twice!!! And category only comes with .h file. I guess you are creating yourself a .m file, that not acceptable.
Private properties cant be accessed outside the class, it can be accessed only from the base class or subclass. That is what the error is.
I can't implement class extension outside the original .m files it seems.
Yes this is abstraction and data hiding of Objective-c !!!
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.
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.