Difference between private instance variable and property in class extension (Objective-c 2.0) - objective-c

What are the differences (if any) between the following Objective-c 2.0 code snippets:
// in MyClass.h
#interface MyClass
#private
NSString *myString;
#end
and
// in MyClass.m
#interface MyClass ()
#property (nonatomic, copy) NSString *myString;
#end
#implementation MyClass
#synthesize myString;
#end

The ivar (first one) is a plain variable that cannot be accessed out of the scope of an implementation of the interface it's created in (if #private directive is used) and has no synthesized accessor methods.
The property (second one) is a wrapped ivar and something that can always be accessed via instantiating a class and has accessor methods synthesized (if #synthesize directive is being used)
MyClass *class = [[MyClass alloc] init];
[class setMyString:#"someString"]; //generated setter
NSString *classString = [class myString]; //generated getter

Related

Readonly, non-mutable, public and readwrite, mutable, private #property: more information?

I want to expose an NSArray to my user (and I want them to only read it), but in my class, I want to use an NSMutableArray.
I tried the following code and it does not raise any warning:
// In the .h
#interface MyClass : NSObject <NSApplicationDelegate>
#property (nonatomic, readonly) NSArray * test ;
#end
and
// In the .m
#interface MyClass ()
#property (nonatomic, strong, readwrite) NSMutableArray * test ;
#end
#implementation MyClass
- (id)init
{
self = [super init];
if (self)
{
self.test = [[NSMutableArray alloc] init] ;
}
return self;
}
#end
But, if I try to access the #property test from within my class, I can use the method addObject:. So, I guess what precedes is not possible.
Why is there no warning as it is?
I don't think that mixing property type would be a good practice. Instead I would create an accessor that returns a copy of the private mutable array. This is more conventional. Please note, don't use self for property access in your -init: method:
// In the .h
#interface MyClass : NSObject <NSApplicationDelegate>
- (NSArray *)test;
#end
// In the .m
#interface MyClass ()
#property (nonatomic, strong) NSMutableArray *aTest;
#end
#implementation MyClass
- (id)init
{
self = [super init];
if (self)
{
_aTest = [[NSMutableArray alloc] init] ;
}
return self;
}
- (NSArray *)test
{
return [self.aTest copy];
}
#end
The #property is just syntax sugar which automatically creates getter/setter methods for you. With the readonly in the .h file only the getter method will be created for the public but by overriding it in the .m file you get both methods in your implementation.
readwrite is the default (see here) so even if leave out readwrite put still have the #property in you implementation file you will get a setter method. It is good practice to explicitly write readwrite then in your .m file so you and other will get a hint that this variable might only be declared read only in the .h file.

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.

Inheriting instance variables in Objective-c

In Objective-c 2.0 why do subclasses need to reference instance variables in parent classes using the self keyword?
Consider this example:
// a.h
#interface MyClass : NSObject
#property (nonatomic, retain) Object *myObject;
#end
// a.m
#implementation MyClass
#synthesize myObject;
#end
// b.h
#interface AnotherClass : MyClass
#end
// b.m
#implementation AnotherClass
- (void) someMethod {
// error
// Object *obj = myObject;
// works
// Object *obj = self.myObject;
}
#end
You haven't actually defined a variable, you only defined a property (which implicitly defines a variable that is private). And since property are just method, you need the dot syntax. Note that self.property is the same as [self property].
To fix this, specify a variable. I'll give you an example where the variable has a different name than the property. Most people chose the same name for both but I like to have them differ so I immediately see which one is meant.
// a.h
#interface MyClass : NSObject {
// Instance variables are "protected" by default, except if you
// use #private or #public.
Object *myObjectVar;
}
#property (nonatomic, retain) Object *myObject;
#end
// a.m
#implementation MyClass
#synthesize myObject = myObjectVar;
#end
// b.h
#interface AnotherClass : MyClass
#end
// b.m
#implementation AnotherClass
- (void) someMethod {
// works
Object *obj = myObjectVar;
// works
obj = self.myObject;
// the same as self.myObject
obj = [self myObject];
}
#end
Note the difference when you assign: if you assign to your variable the object is not retained automatically. But it is retained if you use the property:
myObjectVar = someObject; // not retained, old object not released!
self.myObject = someObject; // old object released, new object retained
[self setMyObject:someObject]; // same as the line above
Edit: Mentioned that the synthesized instance variables are private by default, as noted by #Jason Coco. And #NSGod is right that normal instance variables are protected by default rather than public, fixed that.
They don't, provided you actually declare an instance variable in the superclass, rather than rely on the new runtime's ability to synthesize the instance variable (in addition to synthesizing the accessor methods). See The Objective-C Programming Language: Runtime Difference for more info on instance variable synthesis.
For example, to be able to refer to the instance variable directly, you'd need to change the following:
#interface MyClass : NSObject
#property (nonatomic, retain) Object *myObject;
#end
to:
#interface MyClass : NSObject {
// there is an implied #protected directive here
Object *myObject;
}
#property (nonatomic, retain) Object *myObject;
#end
By default, instance variables are #protected, meaning the class and any subclasses can access the instance variables directly. #protected ivars differ from #public ivars in that you can't access them using ->. #private ivars can only be accessed by the class that declares them. See The Objective-C Programming Language: The Scope of Instance Variables for more info.

Question about class member declarations

I`m new to the Objective-C world, so I have a couple of questions about class member declarations. Please notice the comments in the code below:
In header file I have code such
#interface MyClass : NSObject {
//what we points here ? Object or something else ?
NSString *myString;
}
// In interface we declare NSTring *myString in #property declaration is (NSString *) myString.
// What is the difference here ? Why we don`t use the same declaration as above ?
#property(nonatomic, retain) (NSString *) myString;
#end
The thing you're missing is that instance variables (defined between curly braces) are not accessed from the outside (i.e. other objects). To do that - you have to define a property for the instance var (by using #property keyword) to know how outside objects can access a value of given instance var. Also in implementation file (.m) you have to #synthesize instance variable accessor methods for it's appropriate property. Please note that #property declaration not only defines what it holds (NSString *myString), but also how it's being accessed and set. You can define property as read only (#property (readonly)...) or accessible from few threads at a time (#property (nonatomic)).
Also - if your instance var is named differently from the property it represents to other objects - you must show that in implementation file (#synthesize propertyName=instanveVariableName)
update
MyClass *myInstance = [[MyClass alloc] init];
[myInstance myString]; // returns myString property
Try running above 2 lines of code without #property and you'll see the difference.
Actually you are defining a Property of yar class.#interface MyClass : NSObject {
//public object
#public
NSString *myString;
//private object
NSString *myString2;
}
class structure for obj-c
.h file
#interface MyClass : NSObject {
//Your member variable;
// you member objects;
}
//property declarations
//function declarations
#end
so it should look like
#interface MyClass : NSObject {
NSString *str;
}
#property(nanatomic,retain) NSString *str;
-(void)method;
#end

Objective-C property that is readonly publicly, but has a private setter

I'd like to use the #property syntax to declare a synthesized property that is publicly readonly but has a setter that can be called privately from within the class.
Since it's Objective-C, this basically means that the setFoo: method would be synthesized, but calling it outside of the class itself would result in a warning (unrecognized selector). To trigger the warning I have to declare the property readonly; is there any way to force a synthesized setter that is only available within the class?
I think what you're looking for are called class extensions. You would declare the property read-only in the header:
#interface MyClass : NSObject {
}
#property (readonly, assign) NSInteger myInteger;
#end
Then redeclare in your class extension in the implementation file:
#interface MyClass ()
#property (readwrite, assign) NSInteger myInteger;
#end
#implementation MyClass
#end
For more check out Apple's documentation
I might be late, but without extension i did using the following technique
#interface MyClass : NSObject {
NSString * name;
}
#property (readonly, strong) NSString * name;
#end
on the other hand in implementation file
#implementation MyClass
#synthesize name;
- (id)initWithItems:(NSDictionary *)items {
self = [super init];
if(self)
{
name = #"abc";
}
return self;
}
#end
doing so it will set your value and will be accessible as readonly.
Thanks.