I am attempting to create an abstract class and inherit some of its properties in a subclass. If I leave the properties in the abstract class' header file, all of the properties are accessible. The problem is that the instance of the subclass can also access those properties, which is not always desirable in my case.
For instance, I have a delegate in my abstract class that sends down button presses to its sub class. I realize that this may not be the best way of structuring inheritance, so other suggestions are welcome. However, I would still like to know how my subclass can inherit some properties from its superclass without making all of those properties available in its instance. Thanks in advance!
Here is some example code below:
#interface AbstractClass : UIView
#property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;
#end
…
#protocol ButtonDelegate
#required
- (void) buttonWasPressed;
#end
…
#interface SubClass() <ButtonDelegate>
- (id)init {
self = [super init];
if (self) {
self.buttonDelegate = self;
}
return self;
}
-(void) buttonWasPressed {
[self doSomething];
}
…
#implementation ViewController
- (void)viewDidLoad {
SubClass *subClass = [[SubClass alloc] init];
subClass.buttonDelegate = self; // THIS IS NOT DESIRABLE
}
Do like UIGestureRecognizer does.
All public properties and methods goes into UIGestureRecognizer.h
All protected properties and methods goes into UIGestureRecognizerSubclass.h.
Only import this in the *.m-files. Never include it in any public header.
All private properties and methods goes into *.m-files. Use the #interface ClassName ()
Example https://gist.github.com/hfossli/8041396
how to my subclass can inherit some properties from its superclass
without making all of those properties available in its instance
What is the problem with this?
#import <Foundation/Foundation.h>
#interface Animal : NSObject
{
#protected
NSString *name; // default access. Only visible to subclasses.
}
#end
#implementation Animal
-(NSString*)description {
return name;
}
#end
#interface Cow : Animal
#end
#implementation Cow
-(id)init {
self=[super init];
if (self){
name = #"cow";
}
return self;
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Cow *cow = [Cow new];
NSLog(#"%#", cow); // prints the name through internal access
// error accessing from the outside: NSLog(#"%#", cow.name);
Animal *animal = [Animal new];
// error accessing from the outside: NSLog(#"%#", animal.name);
}
}
Maybe I misunderstood the question, you say
Creating properties only visible to subclass in Objective-C
and then
The problem is that the instance of the subclass can also access those
properties
Which one is it?
Create an empty category on top of your implementation file (.m):
#interface AbstractClass()
#property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;
#end
In that way, your subclass will inherit and can access that property, but not other external classes because it's not in the header.
I don't think there is any way to achieve this using property declaration.
Either a property be visible for all (declared in .h file) or it will be invisible for all (declared in .m file using category)
I guess one way is declaring public/protected variable in .h file class declaration:
#interface AbstractClass : UIView {
...
id<ButtonDelegate>buttonDelegate;
...
}
#end
I am not sure about this, but give a try.
I see one approach that can fit your problem, however, it is pretty rude. Use Antonio's suggestion and create the private category with the property. As you've mentioned, it's scope is limited to the .m file. So you can put your subclasses into that file. This will be hard to read the code if subclasses are huge, but this is the only way for you as far as I understand.
EDIT: well, I have another solution. Copy
#property (nonatomic, strong) id<ButtonDelegate>buttonDelegate;
to all your subclasses. This will give you a warning about the absence of the property's #synthesize, but should work. I'd prefer this, if subclasses wont be changed or added often.
Let me describe how it would work.
We add a property into the Abstract class, and it is hidden for all (even for subclasses):
// .m file
#interface Abstract ()
#property (nonatomic, strong) id<ButtonDelegate> buttonDelegate;
#end
#implementation Abstract
#synthsize buttonDelegate;
#end;
But due to runtime features of Objective-C we still can call for that property, and there will not be any runtime error, only compiler warning.
To get rid of that warning and to add an ability to autocomplete, we add property without #synthsize into all subclasses:
#interface MySubclass : Abstract
#property (nonatomic, strong) id<ButtonDelegate> buttonDelegate;
#end
This will tell the compiler that there is such a property somewhere. There will be also one warning about the absence of #synthesize, but Xcode will still could autocomplete if you write something like
MySubclass *subclass = ...
subclass.butto...
It can not be done. There is no private or protected in objective-c. Stuff declared in the .m file "private" interface is only visible to that class and not in any subclass. Also you can always use your "private" properties/methods from outside if you want, although it would be bad practice to do so.
Related
Let's say I have a class:
#interface MyClass
#end
#implementation MyClass {
myType *_myIvar;
}
#end
And I'd like to expose it for testing. I see two ways to do this:
expose the ivar as a property:
#interface MyClass
// Pragma mark - Expose for testing
#property myIvar;
#end
#implementation MyClass
#end
Use key value coding:
-(void)myTest {
myType *myIvar = [myClass valueForKey:#"_myIvar"];
}
Which method is preferred?
First - you don't test private methods or state of a class for unit-testing, as per TDD best practices.
Having that said, however, sometimes it's the only way to observer possible side-effects. I personally always wrap any ivar of a class with a property. For data which is not supposed to appear in the public interface I put it in the extension inside of the implementation file:
// Implementation file
#interface TDWClass ()
#property (strong, nullable) NSString *tdw_p_message;
#end
P.S. This also helps to maintain certain semantic for the given property (you immediately can see not just storage modifier, but other attributes the property is supposed to follow: like, being read-only, nullability, etc..)
When it comes to testing such a property, this approach helps to conveniently read any "private" property (or accessing a private method) by re-declaring it in a category:
// XCTest file
#interface TDWClass (XCTest)
#property (strong, nullable) NSString *tdw_p_message;
#end
I have read in iOS Programming Fundamentals by Matt Neuberg that instance variables are protected, meaning that other classes, except for subclasses of this one, can't see them.
I have a parent class A where I define an ivar list.
(A.m)
#interface A ()
#end
#implementation A
{
NSArray *list;
}
#end
Class B extends A (B.h)
#import "A.h"
#interface B:A
#end
(B.m)
#interface B ()
#end
#implementation B
list =
...
#end
I want to use ivar list in child class B but the compiler doesn't see that the was reference declared in the parent class. I have tried explicitly using #protected but that doesn't work. I don't want to expose ivar list on the public interface. It's an internal structure that is a common element of all subclasses. How can I do this?
Instance variables that are declared outside a class's public interface (in other words, the #interface section) are private by default. You can add a visibility modifier to the declaration to change the visibility of one or more ivars like so:
#implementation A
{
NSNumber *_ivarWithDefaultVisibility;
#protected
NSArray *_list;
NSString *_anotherIvarWithProtectedVisibility;
}
(Note that according to Apple's Cocoa coding guidelines, ivar names should be prefixed with an underscore.)
This is not the usual way to implement protected properties in ObjC. (#protected and #private are very seldom used in Cocoa.) First, use a property, not an ivar. It will make it much cleaner. Declare it this way:
A.h
#interface A : NSObject
// Public interface goes here
#end
A.m
// Declare the property in a class extension inside the implementation file.
// This is the idiomatic way to create a "private" property.
#interface A ()
#property (nonatomic, readwrite, strong) NSArray *list;
#end
A+protected.h
// Enumerated any methods that should be accessible to subclasses here.
// Properties are just a special way of defining methods.
// The word "Protected" is just a category name.
#interface A (Protected)
#property (nonatomic, readwrite, strong) NSArray *list;
#end
B.h
#import "A.h"
#interface B : A
...
#end
B.m
#import "B.h"
#import "A+protected.h"
// ... now you can use self.list ...
This approach allows you to create any kind of "protected" method, not just properties, and is also the technique used to create "friend" classes.
I am trying to re-struct my project code with an inheritance style, what is the best practice for how to inherit property from parent class and override setter and getter?
I give the demo code, in the demo, ChartModel is a base class in a ChartViewController, and LineChartModel is a sub class of ChartModel in LineChartViewController.
I want to override the setter and getter of LineChartModel *dataModel in sub view controller. Please include any #synthesize and protected instance variable, or if it is automatically generated by compiler, please mark. Thank in advance.
// ChartModel.h
#interface ChartModel : NSObject
-(BOOL)hasData;
#end
// LineChartModel.h
#interface LineChartModel : chartModel
-(void)getLineColor;
#property (nonatomic, strong) NSArray* dataArray;
#end
// ChartViewController.h
#interface ChartViewController: UIViewController
#property (nonatomic, strong) ChartModel *dataModel;
-(void)updateUI;
#end
// ChartViewController.m
#implementation ChartViewController
-(void)updateUI {
if ([self.dataModel hasData]) {
[self.view setHidden:NO];
} else {
self.view.hidden = YES;
}
// setter and getter here
#end
// LineChartViewController.h
#interface LineChartViewController : ChartViewController
// pay attension here, same name but a sub class of chartModel
#property (nonatomic, strong) LineChartModel *dataModel;
#end
// LineChartViewController.m
#implementation LineChartViewController
//override dataModel setter here
//override dataModel getter here
#end
Technically, the only thing you need in the implementation of LineChartViewController is:
#dynamic dataModel;
That tells the compiler that the getter and setter will be supplied in some way it can't immediately see. In actuality, they will be supplied by the superclass.
However, that allows for a problem. A LineChartViewController is-a ChartViewController. That means that an instance of LineChartViewController can be passed to a method or function which is declared to take a ChartViewController and that method/function is entitled to do anything to it that is allowed by the interface of ChartViewController. That includes assigning an instance of ChartModel (not LineChartModel) to its dataModel property. Presumably, LineChartViewController will break if its dataModel property is not a LineChartModel.
In technical terms, your design violates the Liskov substitution principle.
It's not a fix for the design issue, but you can catch the problem at run time if it happens, by implementing an override of the setter like this:
- (void) setDataModel:(LineChartModel*)dataModel
{
if (dataModel && ![dataModel isKindOfClass:[LineChartModel class]])
{
NSString* reason = [NSString stringWithFormat:#"%# is not a valid dataModel for LineChartViewController; it must be a kind of LineChartModel", dataModel];
[[NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil] raise];
}
[super setDataModel:dataModel];
}
I want to access super class iBoutlets objetcs in my subclass. Is that possible?. I am trying in the following way but am always getting nil.
Here is my code:
My super class
#import <UIKit/UIKit.h>
#interface SuperClassA : UIViewController {
}
#property (weak, nonatomic, getter=getDummyView) IBOutlet UIView *dummyView;
#end
#implementation SuperClassA
- (void)viewDidLoad {
[super viewDidLoad];
SubClassB *obj = [SubClassB new];
[obj printSuperClassiBouletObject];
}
#end
my subclass:
#import <UIKit/UIKit.h>
#interface SubClassB : SuperClassA {
}
#end
#implementation SubClassB
-(void)printSuperClassiBouletObject
{
NSLog(#"view: %#", [self getDummyView]);
}
#end
The above code gives me nil value always. Any idea how to get the actual iBoutlet object?. But when i pass the iBoutlet as an function argument then the object was not nil. In the super class i tried strong property, using #synthesize in implementation file but no helps. Any help that really might be appreciated.
My guess is that the problem is here:
SubClassB *obj = [SubClassB new];
[obj printSuperClassiBouletObject];
New will use default initializer, that will try to load nib with your class name, which is SubClassB. Do you have SubClassB.xib that set the outlet your project? If not, then SubClassB will be initialized with empty properties, by the default objc initializer.
Try this:
SubClassB *obj = [[SubClassB alloc] initWithNibName:#"SuperClassA"]; //or xib name that SuperClassA uses to initialize
There's nothing special about IBOutlet relative to the scope rules. You can inherit any property from super by including its declaration in the subclass. The simple way to do this is by placing those property declarations in the superclass's public interface (in superclass.h). Since we know that the subclass must import that, we know that everything in there will be available to the subclass.
A more complicated arrangement is required if you'd like the subclass to access the property but not other classes. These "protected" declarations need to go into a third header file that only the super and subclass import.
In other words... (simple case):
// MySuper.h
#interface MySuper : NSObject
#property (weak,nonatomic) IBOutlet UIView *someOutlet;
#end
// MySub.h
#import "MySuper.h"
#interface MySub : MySuper
// stuff for my sub
#end
Now both MySuper and MySub can refer to someOutlet
For "protected", something like this:
// MySuper.h
#interface MySuper : NSObject
// only public stuff here
#end
// MySuper-protected.h
#interface MySuper ()
#property (weak,nonatomic) IBOutlet UIView *someOutlet;
#end
// MySuper.m
#import "MySuper.h"
#import "MySuper-protected.h"
// MySub.h
// same as simple case
// MySub.m
#import "MySub.h"
#import "MySuper-protected.h"
Finally, i was able to achieve this by using this way!!!!
In my subclass i changed the method to:
-(void)printSuperClassiBouletObject:(SuperClassA*)superClassObj
and in my super class i am calling this way:
SubClassB *obj = [SubClassB new];
[obj printSuperClassiBouletObject:self];
And in my subclass i was access the super class objects and its variable from the super class instance it self.
Thanks for all the help !!!!!! :-)
Normally we use
#interface interface_name : parent_class <delegates>
{
......
}
#end
method in .h file and in .m file we synthesis the properties of variables declared in .h file.
But in some code, this #interface.....#end method is kept in the .m file also. What does it mean? What is the difference between them?
Also give some words about getters and setters for the interface file that is defined in .m file...
It's common to put an additional #interface that defines a category containing private methods:
Person.h:
#interface Person
{
NSString *_name;
}
#property(readwrite, copy) NSString *name;
-(NSString*)makeSmallTalkWith:(Person*)person;
#end
Person.m:
#interface Person () //Not specifying a name for the category makes compiler checks that these methods are implemented.
-(void)startThinkOfWhatToHaveForDinner;
#end
#implementation Person
#synthesize name = _name;
-(NSString*)makeSmallTalkWith:(Person*)person
{
[self startThinkOfWhatToHaveForDinner];
return #"How's your day?";
}
-(void)startThinkOfWhatToHaveForDinner
{
}
#end
The 'private category' (the proper name for a nameless category is not 'private category', it's 'class extension') .m prevents the compiler from warning that the methods are defined. However, because the #interface in the .m file is a category you can't define ivars in it.
Update 6th Aug '12: Objective-C has evolved since this answer was written:
ivars can be declared in a class extension (and always could be - the answer was incorrect)
#synthesize is not required
ivars can now be declared in braces at the top of #implementation:
that is,
#implementation {
id _ivarInImplmentation;
}
//methods
#end
The concept is that you can make your project much cleaner if you
limit the .h to the public interfaces of your class, and then put
private implementation details in this class extension.
when you declare variable methods or properties in ABC.h file , It
means these variables properties and methods can be access outside the
class
#interface Jain:NSObject
{
NSString *_name;
}
#property(readwrite, copy) NSString *name;
-(NSString*)makeSmallTalkWith:(Person*)jain;
#end
#Interface allows you to declare private ivars, properties and
methods. So anything you declare here cannot be accessed from outside
this class. In general, you want to declare all ivars, properties and
methods by default as private
Simply say when you declare variable methods or properties in ABC.m
file , It means these variables properties and methods can not be
access outside the class
#interface Jain()
{
NSString *_name;
}
#property(readwrite, copy) NSString *name;
-(NSString*)makeSmallTalkWith:(Person*)jain;
#end
you can even create other classes in .m file,
for instance other small classes which inherit from the class declared in .h file but having some slight different behaviour.
You could use this in a factory pattern