Difference between #property (nonatomic, readonly) and #property inside class extension? - objective-c

I have an Objective-c class "MyClass". In MyClass.m I have a class extension that declares a CGFloat property:
#interface MyClass ()
#property (nonatomic) CGFloat myFloat;
#end
#implementation MyClass
#synthesize myFloat;
//...
#end
What changes (if anything) when the property is declared using the readonly keyword?
#interface MyClass ()
#property (nonatomic, readonly) CGFloat myFloat;
#end
#implementation MyClass
#synthesize myFloat;
//...
#end
Perhaps in the first case I can say self.myFloat = 123.0; and CGFloat f = self.myFloat; inside MyClass? Then in the second case the readonly keyword prevents the assignment self.myFloat = 123.0; but allows the read CGFloat f = self.myFloat;

The option readonly means that only the getter method is being declared for this property. Thus, without a setter, it can't be modified via myObject.myFloat=0.5f;
If you don't declare it readonly, it's read write by default.
Declaring your property via () extension does not modify the access mode but it modifies the scope; it will be a "private" property.

#synthesize uses the #property definition to generate the appropiate getter/setter for the iVar. When you specify readonly, no setter is generated. This is not strictly enforced as you can write your own setter if you choose (though that doesn't make a ton of sense).
Declaring the property in a category simply defines the scope of the property to be within that category.

You're right, declaring your property as readonly you tell compiler to not generate setter method automatically and so self.myFloat = 123.0; will be illegal (unless you create that method manually).

Related

Declare a static property in an Objective-C class interface

I'm defining an Objective-C class:
#interface MyRequest : NSObject
#property (strong, nonatomic, readonly) NSDecimalNumber *myNumber;
#property (strong, nonatomic, readonly) CommConfig *commConfig;
#property (nonatomic, assign, readonly) BOOL debug;
How do I make commConfig a static variable? When I use the 'class' keyword, the compiler gives me the following warning:
Class property 'commConfig' requires method 'commConfig' to be defined - use #dynamic or provide a method implementation in this class implementation
And the constructor doesn't recognize this line anymore:
_commConfig = commConfig
If not implemented by the programmer instance properties are automatically implemented by the compiler - an instance variable allocated and getter and/or setter methods written. Class properties are never automatically implemented, you therefore need to declare the static backing variable and define the getter. In your #implementation add:
static CommConfig *_commConfig;
+ (CommConfig *) commConfig { return _commConfig; }
You can call the backing anything you wish, e.g. to follow a naming convention for global/static variables.
HTH

#property and #synthesize

I'm very new to Objective C. (Two days now). When read about #synthesize, it seemed to overlap with my understanding #property (which I thought I understood) ... So, some details need to be ironed out in my mind ... it's bugging me.
Please correct me if I'm wrong about differences of #property and #synthesize:
If you declare a #property in your #interface, then you're telling the world that users can expect to use standard getters and setters for that property. Futhermore, XCode will make generic getters and setters for you. ... BUT, To what degree does that happen with the #property declaration? ( I.E. does that mean "completely" ... like unseen declarations for it in your #interface, and also unseen code in your #interface?
-Or-
Does #property take care of the unseen code declarations in your #interface only - whereas #synthesize takes care of the unseen code implementation in your #implementation section? )
First, note that the latest version of Xcode does not require #synthesize at all anymore. You can (and should) just omit it. That said, here's what the pieces do.
#property is a declaration of accessors. It is just a declaration. There is very little difference between the following:
#property (nonatomic, readwrite, strong) NSString *something;
vs.
- (NSString *)something;
- (void)setSomething:(NSString)aSomething;
The main difference is that declaring these methods using #property lets the compiler automatically generate (synthesize) the implementations for you. There is no requirement that you let the compiler do it for you. You are absolutely free to implement something and setSomething: by hand, and it is common to do. But, if you don't implement them by hand, the compiler will automatically create an ivar for you called _something and create a reasonable implementation for the getter and setter.
In older versions of Xcode, you had to explicitly request the auto-generation using the #synthesize keyword. But that is no longer required. Today, the only reason to use #synthesize is if you want the ivar to have a non-standard name (never do that).
A key point here is that the methods something and setSomething: are just methods. There is nothing magical about them. They're not special "property methods." They're just methods that by convention access a piece of state. That piece of state is often stored in an ivar, but does not need to be.
To be even more clear: object.something does not mean "return the ivar named _something from object." It means "return the result of [object something], whatever that does." It is common for that to return the value of an ivar.
You should declare all of your state (internal and external) using #property declarations, and you should avoid directly declaring ivars. You should also always access your properties via their accessors (self.something), except in the init and dealloc methods. In init and dealloc, you should directly use the ivar (_something).
#property declares a property on your class with whatever atomicity and setter semantics you provide.
With Xcode 4.4, autosynthesis is available wherein you are provided with a backing ivar from your property without declaring it in #synthesize. This ivar has the form of _propertyName where your property name is propertyName.
Objective-C #property and #synthesize
#property
generates get/set method
today(from Xcode v4.4 with the LLVM v4.0) #property additionally uses #synthesize inside
#synthesize propertyName = _propertyName
#synthesize:
generates a new iVar or link with existing iVar
generates an implementation of the get/set method with an appropriate iVar
[Case when #synthesize can be used]
#property
#interface SomeClass : NSObject
#property NSString *foo;
#end
//generated code
#interface SomeClass : NSObject
- (NSString *)foo;
- (void)setFoo:(NSString)newFoo;
#end
#synthesize pattern
#synthesize <property_name> = <variable_name>;
//Using
//1. Specify a variable. New variable(variableName) will be generated/linked with existing
#synthesize propertyName = variableName
//if variableName is not exist it generates:
//NSString *variableName;
//read access
NSString *temp = variableName;
//2. Default. New variable(propertyName - the same name as a property) will be generated/linked with existing
#synthesize propertyName
//is the same as
//#synthesize propertyName = propertyName
//if propertyName is not exist it generates:
//NSString *propertyName;
//read access
NSString *temp = propertyName;
//if you specify not-existing <property_name> you get
//Property implementation must have its declaration in interface '<class_name>' or one of its extensions
previously you had to use next syntax:
#interface SomeClass : NSObject
{
//1. declare variable
NSString *_foo;
}
//2. create property
#property NSString *foo;
#end
#implementation SomeClass
//3. link property and iVar
#synthesize foo = _foo;
#end
But today you can use next syntax
#interface SomeClass : NSObject
//1. create property
#property NSString *foo;
#end
Next, the same code, will be generated for both cases
#interface SomeClass : NSObject
{
//variable
NSString *_foo;
}
//getter/setter
- (void)setFoo:(NSString *)newFoo;
- (NSString *)foo;
#end
#implementation SomeClass
- (void)setFoo:(NSString *)newFoo
{
_foo = newFoo;
}
- (NSString *)foo
{
return _foo;
}
#end

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.

private property in Objective C

Is there a way to declare a private property in Objective C? The goal is to benefit from synthesized getters and setters implementing a certain memory management scheme, yet not exposed to public.
An attempt to declare a property within a category leads to an error:
#interface MyClass : NSObject {
NSArray *_someArray;
}
...
#end
#interface MyClass (private)
#property (nonatomic, retain) NSArray *someArray;
#end
#implementation MyClass (private)
#synthesize someArray = _someArray;
// ^^^ error here: #synthesize not allowed in a category's implementation
#end
#implementation MyClass
...
#end
I implement my private properties like this.
MyClass.m
#interface MyClass ()
#property (nonatomic, retain) NSArray *someArray;
#end
#implementation MyClass
#synthesize someArray;
...
That's all you need.
A. If you want a completely private variable. Don't give it a property.
B. If you want a readonly variable that is accessible external from the encapsulation of the class, use a combination of the global variable and the property:
//Header
#interface Class{
NSObject *_aProperty
}
#property (nonatomic, readonly) NSObject *aProperty;
// In the implementation
#synthesize aProperty = _aProperty; //Naming convention prefix _ supported 2012 by Apple.
Using the readonly modifier we can now access the property anywhere externally.
Class *c = [[Class alloc]init];
NSObject *obj = c.aProperty; //Readonly
But internally we cannot set aProperty inside the Class:
// In the implementation
self.aProperty = [[NSObject alloc]init]; //Gives Compiler warning. Cannot write to property because of readonly modifier.
//Solution:
_aProperty = [[NSObject alloc]init]; //Bypass property and access the global variable directly
It depends what you mean by "private".
If you just mean "not publicly documented", you can easily enough use a class extension in a private header or in the .m file.
If you mean "others are not able to call it at all", you're out of luck. Anyone can call the method if they know its name, even if it is not publicly documented.
As others have indicated, (currently) there is no way to truly declare a private property in Objetive-C.
One of the things you can do to try and "protect" the properties somehow is to have a base class with the property declared as readonly and in your subclasses you can redeclare the same property as readwrite.
Apple's documentation on redeclared properties can be found here: http://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html#//apple_ref/doc/uid/TP30001163-CH17-SW19

Initializing a readonly property

I was trying to create a property which is readonly. I wanted to initialize with a value from the class creating an instance of this class, e.g.
#property (retain,readonly) NSString *firstName;
And I tried to initialize it like this:
-(id)initWithName:(NSString *)n{
self.firstName = n;
}
Once I did this, the compiler reported an error that the readonly property cannot be assigned. So how can i do this ?
Either assign to the instance variable directly (don't forget to add a retain or copy if you need it) or redeclare the property in a private class extension. Like this:
In your .h file:
#property (readonly, copy) NSString *firstName;
In your .m file:
#interface MyClass ()
// Redeclare property as readwrite
#property (readwrite, copy) NSString *firstName;
#end
#implementation MyClass
#synthesize firstName;
...
Now you can use the synthesized setter in your implementation but the class interface still shows the property as readonly. Note that other classes that import your .h file can still call -[MyClass setFirstName:] but they won't know that it exists and will get a compiler warning.
Don't use the synthesized setter:
firstName = [n retain]; //Or copy
It is generally advised to bypass the setters in any init and dealloc methods anyway.
You can directly access the property with:
_firstName = n;
The setter method that self.firstName = n implies will not be synthesized because you specified readonly in #property (retain,readonly) NSString *firstName;, hence the compiler error.