Private v/s Public class properties in Objective C - objective-c

Trying to make my OOP fundamentals strong based of objective C. Sorry if my explanation is too long.
I have 3 classes in my app as Class A, Class B and Class C objective C classes.
I have a property of class A in my implementation of class B i.e. it makes private property of class B.
// implementation Class B
#interface ClassB ()
#property (nonatomic, strong) ClassA *classA;
#end
I create an instance of class B in one of my class C methods and try to access class A property through class B's instance in class C.
// implementation Class C
#interface ClassC ()
#property (nonatomic, strong) ClassB *classB;
#end
#implementation ClassC
- (void)someMethod
{
NSString *string = [[NSString alloc] init];
classB = [[ClassB alloc] init];
string = classB.classA.displayString; //get an error here - ClassB doesn't have classA.
}
#end
To avoid the error I moved the classA property from implementation to header in ClassB.
// header Class B
#interface ClassB : NSObject
#property (nonatomic, strong) ClassA *classA;
#end
But I am worried that anybody class can create an instance of class B, access classA property and can then use/modify the properties which are part of class A.
Question: Is it a good style to move the classA property to the header file of Class B so I can use it in Class C or should I create a method in Class B which returns me whatever I need from class A? Something like:
#implementation ClassB
- (NSString*)displayStringOfClassA
{
classA = [[ClassA alloc] init];
return self.classA.displayString;
}
#end

In ClassB.h:
#interface ClassB: NSObject
#property (nonatomic, strong, readonly) ClassA *classA;
#end
In ClassB.m:
#interface ClassB()
#property (nonatomic, strong, readwrite) ClassA *classA;
#end
Also, strong and readwrite are default modifiers - you can get rid of them. However, they improve code readability.
EDIT: if you want to forbid access for ClassA properties - do the same trick. Suggested above code will forbid only to modify classA property of classB. For ClassA's displayString:
In ClassA.h:
#interface ClassA: NSObject
#property (nonatomic, strong, readonly) NSString *displayString;
#end
In ClassA.m:
#interface ClassA()
#property (nonatomic, strong, readwrite) NSString *displayString;
#end

I would suggest a readonly string property in ClassB.h.
ClassB.h:
#property (nonatomic, readonly) NSString *classAString;
ClassB.m:
- (NSString *) classAString
{
return self.classA.displayString;
}
This acts as a "getter" method for the particular string you need, and avoids others getting access to classA.
Edit:
Others suggested adding classA as a readonly property in ClassB.h. This will still allow modification of classA properties, it will only guarantee that classA is not reassigned to another ClassA instance.

Sounds like you want "framework", not "private" #property() declarations.
To do this, create a file like:
ClassA_Private.h
That contains your #property() declaration.
Then #import "ClassA_Private.h" in both your ClassA.m (prior to the #implementation) and in any subclasses that want to use that #property.
This was a secondary design consideration when creation class extensions; adding the ability to have #property declarations that are fully accessible across subclasses and/or within frameworks without being exposed externally. While you can't create a framework for an iOS targeted application, the same functionality still applies.

Something like string = objectB.objectA.displayString; is violating the Law of Demeter. Designing according to the Law of Demeter results in more maintainable and adaptable software.
You should always try to not talk to strangers. That is in your example: self talks to objectB and objectB talks to objectA but self shouldn't talk to objectA because it's a stranger.

Related

Objective-C: Accessing a property of another class

Let's assume I have two classes (ClassA, ClassB)
ClassA.h
#interface ClassA : SomeSuperClass
#property (some Attributes) ClassB *classB;
#property (some Attributes) NSString *someString;
#end
Now my problem:
I want to access the NSString someString in the classB instance. Whats the best way to achieve this?
I was thinking of the two solutions:
Creating a protocol in classB and let classA respond to this protocol. The only method in this protocol would be to access/change this string.
The second solution I came up with is give this string as a parameter after creating an instance of classB, then classB can store the reference to it and can change it whenever it wants.
Both of solution seem working to me (didn't test it, just assume it), but also they seem to me a bit of an overkill (especially the first one)
Let me know whats the best way to do this, Thanks.
If you made a weak reference to B's parent (A), then you could safely access it's properties without resorting to overkill or creating retain cycles.
#interface ClassA : SomeSuperClass
#property (some Attributes) ClassB *classB;
#property (some Attributes) NSString *someString;
#end
#implementation
-(id)init {
if (self = [super init]) {
_classB = [[ClassB alloc]initWithParent:self];
}
}
#end
#class ClassA;
#interface ClassB : SomeSuperClass
#property (nonatomic, weak) ClassA *classA;
-(id)initWithParent:(ClassA*)parent;
#end
#import "ClassA.h"
#implementation
-(id)initWithParent:(ClassA*)parent {
if (self = [super init]) {
_classA = parent;
NSLog(#"%#", self.classA.someString); //perfectly legal
}
}
#end

property public and private but not protected?

I'm learning the objective C language and i ask a simple question,
when i do that :
// ParentClass.h
#interface ParentClass : NSObject
#property (read, strong) NSString *parentPublicStr;
#end
// ParentClass.m
#interface ParentClass ()
#property (readwrite, strong) NSString *parentPrivateStr;
#end
#implementation ParentClass
#synthesize parentPublicStr;
#synthesize parentPrivateStr;
#end
// Subclass SubClass.h
#interface SubClass : ParentClass
- (void) test;
#end
#implementation SubClass
- (void) test
{
// Its not possible to do that : [self setParentPrivateStr:#"myStrin"]
// And for parentPublicStr, it is public property so not protected, because i can change the value
// in main.c, and it's so bad..
}
#end
I would like create a property that is protected :x
Thx you. (Sorry for my english)
Objective-C does not provide for protected methods/properties. See this question.
Edit: Also see this answer. You can still practice encapsulation by declaring the property in a class extension and including the extension in subclasses.
You can manually create an ivar for the property as long as you use the same name prefixed with an underscore:
#interface ParentClass : NSObject
{
#protected
NSString* _parentPublicStr;
}
#property (read, strong) NSString *parentPublicStr;
#end
That makes the synthesized ivar for the property #protected (default is #private) and subclasses can then use the super class' ivar directly.

Subclass of class with synthesized readonly property cannot access instance variable in Objective-C

In the superclass MyClass:
#interface MyClass : NSObject
#property (nonatomic, strong, readonly) NSString *pString;
#end
#implementation MyClass
#synthesize pString = _pString;
#end
In the subclass MySubclass
#interface MySubclass : MyClass
#end
#implementation MySubclass
- (id)init {
if (self = [super init]) {
_pString = #"Some string";
}
return self;
}
The problem is that the compiler doesn't think that _pString is a member of MySubclass, but I have no problem accessing it in MyClass.
What am I missing?
The instance variable _pString produced by #synthesize is private to MyClass. You need to make it protected in order for MySubclass to be able to access it.
Add an ivar declaration for _pString in the #protected section of MyClass, like this:
#interface MyClass : NSObject {
#protected
NSString *_pString;
}
#property (nonatomic, strong, readonly) NSString *pString;
#end
Now synthesize the accessors as usual, and your variable will become accessible to your subclass.
I am familiar with this problem. You synthesize the variable in your .m class, so it is not imported along with the header since the _pString variable will be created as part of the implementation, and not the interface. The solution is to declare _pString in your header interface and then synthesize it anyway (it will use the existing variable instead of creating a private one).
#interface MyClass : NSObject
{
NSString *_pString; //Don't worry, it will not be public
}
#property (nonatomic, strong, readonly) NSString *pString;
#end
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 MyClass+Protected.h file, which needs to be included in MyClass.m and MySubclass.m.
Then, in this new file, you redefine the property as readwrite.
#interface MyClass ()
#property (strong, readwrite) NSString * pString;
#end
This alternative allows you to use the accessor self.pString rather than the ivar _pString.
Note: you still need to keep the definition of pString in your MyClass.h as is.

Objective-C: How do you access parent properties from subclasses?

If I have this class defined, how do I access the someObject property in subclasses without compiler errors?
#interface MyBaseClass
// someObject property not declared here because I want it to be scoped
// protected. Only this class instance and subclass instances should be
// able to see the someObject property.
#end
// This is a private interface extension...properties declared here
// won't be visible to subclasses. However, I don't see any way to
// declare protected properties...
#interface MyBaseClass (private)
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
#interface MySubclass : MyBaseClass
#end
#implementation MySubclass
- (id) init {
// Try to do something with the super classes' someObject property.
// Always throws compile errors.
// Semantic Issue: Property 'someObject' not found
// object of type 'MySubclass *'
self.someObject = nil;
}
#end
I'm obviously not understanding how inheritance works in objective-c. Could someone enlighten me?
The solution you're after is to declare the MyBaseClass private property in a class extension:
#interface MyBaseClass ()
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
You are then free to make that declaration both in MyBaseClass and in MySubclass. This lets MySubclass know about these properties so that its code can talk about them.
If the repetition bothers you, put the class extension in a .h file of its own and import it into both .m files.
I will give an example from my own code. Here is MyDownloaderPrivateProperties.h:
#interface MyDownloader ()
#property (nonatomic, strong, readwrite) NSURLConnection* connection;
#property (nonatomic, copy, readwrite) NSURLRequest* request;
#property (nonatomic, strong, readwrite) NSMutableData* mutableReceivedData;
#end
There is no corresponding .m file and that's all that's in this file; it is, as it were, purely declarative. Now here's the start of MyDownloader.m:
#import "MyDownloader.h"
#import "MyDownloaderPrivateProperties.h"
#implementation MyDownloader
#synthesize connection=_connection;
#synthesize request=_request;
#synthesize mutableReceivedData=_mutableReceivedData;
// ...
And here's the start of its subclass MyImageDownloader.m:
#import "MyImageDownloader.h"
#import "MyDownloaderPrivateProperties.h"
Problem solved. Privacy is preserved, as these are the only classes that import MyDownloaderPrivateProperties.h so they are the only classes that know about these properties as far as the compiler is concerned (and that's all that privacy is in Objective-C). The subclass can access the private properties whose accessors are synthesized by the superclass. I believe that's what you wanted to accomplish in the first place.
that's how you access them. how you declare them is what's biting you:
#interface MyBaseClass : NSObject
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
this is the normal way to declare a new objc class.
by adding the parentheses (instead of declaring the superclass - NSObject in this case), you have declared a class extension, which is probably not visible to the subclass (via inclusion).
you will probably never need to declare a root class in objc:
#interface MyBaseClass // << superclass omitted
#property (nonatomic, readwrite, retain) NSObject *someObject;
#end
NSObject (or a subclass of, assuming you're target apple's systems) should be the base class unless you're very experienced and know what a root class is for.
class extensions are often used to 'simulate' private interfaces. by simulate, the compiler doesn't enforce this, as it would be enforced in other languages. for example, all messages are still dynamic, although the subclass may (unknowingly) override methods in your extensions, if declared with the same selector.
Judging by the () after your base class name, it looks like you are declaring a private interface extension within your class implementation, is this the case? If so the variable will only be accessible from within that class implementation.
Does your MyBaseClass inherits from NSObject directly?
If so, you need to declare the someObject property in your interface file, as in:
#interface MyBaseClass : NSObject
{
}
#property (nonatomic, retain) NSObject *someObject;
And then synthesize it like you are already doing.
This is an alternative that meets most of the objectives.
In your header, define the interface
#interface MyBaseClass : NSObject {
NSObject *inheritableObject;
}
#property (readonly) NSObject *inheritableObject;
Now you can edit the inheritableObject in MyBaseClass as well as in any Class that inherits from MyBaseClass. However, from the outside, it is readonly. Not private as in the case of #interface MyBaseClass(), but protected from uncontrolled changes.
super.someObject = nil;. Inheritance means MyBaseClass is your super class.

Friend classes in Objective-C

Is there any way to create something like friend classes in Objective-C?
First declare a "private property" using the standard class extension method:
// VisualNotePlayer.h
#interface VisualNotePlayer : NSObject<NotePlayer>{
#private
UIView *_currentView;
}
// VisualNotePlayer.m
#interface VisualNotePlayer()
#property (nonatomic, retain) UIView *currentView;
#end
#implementation VisualNotePlayer
#synthesize currentView=_currentView;
...
#end
Then recreate the properties in a category:
// VisualNotePlayer+Views.h
#interface VisualNotePlayer(Views)
#property (nonatomic, retain) UIView *currentView;
#end
This interface is only accessible to those who import VisualNotePlayer+Views.h
There is no such thing as a friend class in ObjC.
And to access a private variable of another class you don't even need to be declared as a friend. For example, you can use the runtime functions
id the_private_ivar;
object_getInstanceVariable(the_object, "_ivar_name", &the_private_ivar);
to get the_object->_ivar_name, bypassing compiler checks.