I'd like to use properties for my instance variables, but in many cases, I only want the class itself to have access to the setter. I was hoping I could do something like this:
Foo.h:
#interface Foo {
NSString *bar;
}
#property (readonly) NSString *bar;
#end
Foo.m:
#import "Foo.h"
#interface Foo ()
#property (copy) NSString *bar;
#end
#implementation Foo
#synthesize bar;
#end
But this generates a warning:
Foo.m:4: warning: property ‘bar’ attribute in ‘Foo’ class continuation does not match class ‘Foo’ property
I can see what it's complaining about, but it still seems like a useful idiom. Is there some other way to accomplish this without writing my own setters?
Your approach is correct, but the redeclaration of #property bar in the class extension must match the original declaration except for readwrite vs. readonly. So this will work:
Foo.h
#interface Foo {
NSString *bar;
}
#property (copy,readonly) NSString *bar;
#end
Foo.m:
#import "Foo.h"
#interface Foo ()
#property (copy,readwrite) NSString *bar;
#end
#implementation Foo
#synthesize bar;
#end
(recall that the default is assign for properties, not copy).
When Barry says "the default is retain for properties" he means that retain should be specified like this:
#property (retain) NSDate *endDate;
If they are left like this:
#property NSDate *endDate;
assign is assumed by the compiler.
Related
You can declare a variable like this.
Case1:
#interface MyClass : NSObject
{
NSString *str;
}
#end
Also, if you want to set its property, you can do
Case2:
#interface MyClass : NSObject
{
NSString *str;
}
#property (nonatomic, strong) NSString *str;
#end
And in the .m,
#synthesize str;
My understanding with the difference between Case 1 and Case 2 is that synthesized and propertied variables in Case 2 can be accessed from another class when this another class instantiates this class.
What are other differences between Case 1 and 2? Say when these variables are just used only in its .m file. The fact that you are setting 'str' properties probably makes a difference, but how? If you don't set property, how are they going to be released with ARC?
The differences are fairly confusing in this case because of the way it is set up.
Also it is using what is now old practises.
The new suggested way of doing this (suggested by Apple) is to do this...
MyClass.h
#interface MyClass : NSObject
#property (nonatomic, strong) NSString *str;
#end
MyClass.m
#import "MyClass.h"
#implementation MyClass
#end
You no longer need the #synthesize as Xcode (since 4.5) will auto generate these for you.
Doing this sets up the property called str and an iVar called _str.
You now no longer need to worry about defining multiple ivars and properties etc... Just use the property and that's it done.
An example setter method for the property str would look like this...
- (void)setStr:(NSString*)str
{
_str = str;
}
The following codes crashed:
#interface AppDelegate (PrivateMethods)
#property (nonatomic, strong) NSString * name;
#end
#implementation AppDelegate
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.name = #"foobar";
...
Error is:
'-[AppDelegate setName:]: unrecognized selector sent to instance 0x6d73df0'
When I change
#interface AppDelegate (PrivateMethods)
to
#interface AppDelegate ()
Then it is okay, what would be the reason?
Update: As answered below, since I must use class extension for this purpose, now this question become: Is the use of class extension to declare private methods acceptable?
e.g.
#interface AppDelegate ()
- (void) start;
#property (nonatomic, strong) NSString * name;
#end
Class extension is basically used to enhance the public property variable. Suppose you have exposed readonly object, or getter method of any variable, then you have make the same object as readwrite in extension.Whereas Category is only used to enhance the method/functionality of class.
check this
#interface MyClass : NSObject
// property here is used as readonly.
#property (retain, readonly) float value;
#end
// Private extension, typically hidden in the main implementation file.
#interface MyClass ()
#property (retain, readwrite) float value;
#end
or
#interface MyClass : NSObject
// here we have exposed getter method of private instance.
- (float) value;
#end
// Private extension, typically hidden in the main implementation file.
#interface MyClass ()
#property (retain, strong) float value;
#end
One is a category, the other is a class extension. If you want to add properties to an existing class, you need to use the latter.
This is the right approach:
#interface AppDelegate ()
#property (nonatomic, strong) NSString * name;
#end
In some of the Apple Iphone examples, some of the properties are declared in the header file and some properties in the implementation file. For example in the Siesmic XML examples
ParseOperation.h
#interface ParseOperation : NSOperation {
NSData *earthquakeData;
#private
NSDateFormatter *dateFormatter;
// these variables are used during parsing
Earthquake *currentEarthquakeObject;
Contact *currentContactObject;
NSMutableArray *currentParseBatch;
NSMutableString *currentParsedCharacterData;
BOOL accumulatingParsedCharacterData;
BOOL didAbortParsing;
NSUInteger parsedEarthquakesCounter;
}
#property (copy, readonly) NSData *earthquakeData;
#end
ParseOperation.m
#interface ParseOperation () <NSXMLParserDelegate>
#property (nonatomic, retain) Earthquake *currentEarthquakeObject;
#property (nonatomic, retain) NSMutableArray *currentParseBatch;
#property (nonatomic, retain) NSMutableString *currentParsedCharacterData;
#property (nonatomic, retain) Contact *currentContactObject;
#end
What is the use of the additional interface declaration in the implementation file ?
That’s simply a difference between a public and a private class interface. The header describes the public interface, but some of the properties are only meant to be used by the class itself, not by its collaborators. These private properties are usually declared the way you described, as a category or a class extension inside the implementation file.
// Foo.h – the public interface
#interface Foo : NSObject {…}
// Collaborators can only read bar.
#property(readonly) int bar;
#property(readonly) int baz;
#end
// Foo.m
#import "Foo.h"
// Private interface
#interface Foo ()
// Inside class implementation we can also change bar.
#property(assign) int bar;
#property(assign) int other;
#end
#implementation Foo
#synthesize bar, baz, other;
…
#end
We declare properties using the #property keyword and synthesize it in the implementation file. My question is,
What if I declare a property using the #property keyword and also declare a variable in the interface block with the same name? For example, consider the following code,
Interface:
#interface myClass : NSObject {
NSString *myClass_name; // LINE 1
}
#property(nonatomic, retain) NSString *myClass_name; // LINE 2
#end
Implementation:
#implementation myClass
#synthesize myClass_name // LINE 3
#end
Declaring myClass_name in LINE 1 will make any problem? Like any reference problem or any unnecessary memory consumption problem?
No, in fact, declaring properties like that expects it. You could replace your declaration to:
#interface MyClass : NSObject {
NSString *ivar;
}
#property (nonatomic, retain) NSString *myClass_name;
#end
And then change your implementation to
#implementation MyClass
#synthesize myClass_name = ivar;
#end
(If you don't specify the = some_ivar, it will assume the ivar has the same name as the property.)
You always need to have the following lines:
Declaration of the property (Line 2)
Synthesization of the property (Line 3)
When you synthesize the property, if you do not specify which ivar to use (by using =ivar at the end), it will assume that there is an ivar with the same name as the property.
Declaring properties and synthesizing it will not create any reference problem in your case.
Doing this will create accessor and setter methods for your instance variable in your class.
If the variable names in the property and the one declared in the class, then the xcode will refer both as a single variable.
Line 3 and Line 4 are must. Line 1 is optiona
I got the following content from Apple's doc for Declared Properties. I am posting it here, so that it may be helpful for someone in future.
Runtime Difference
In general the behavior of properties is identical on all runtimes (see Runtime Versions and Platforms in Objective-C Runtime Programming Guide). There is one key difference: the modern runtime supports instance variable synthesis whereas the legacy runtime does not.
For #synthesize to work in the legacy runtime, you must either provide an instance variable with the same name and compatible type of the property or specify another existing instance variable in the #synthesize statement. With the modern runtime, if you do not provide an instance variable, the compiler adds one for you. For example, given the following class declaration and implementation:
#interface MyClass : NSObject {
float sameName;
float otherName;
}
#property float sameName;
#property float differentName;
#property float noDeclaredIvar;
#end
#implementation MyClass
#synthesize sameName;
#synthesize differentName=otherName;
#synthesize noDeclaredIvar;
#end
the compiler for the legacy runtime would generate an error at #synthesize noDeclaredIvar; whereas the compiler for the modern runtime would add an instance variable to represent noDeclaredIvar.
The following is the Object-oriented way:
DeclaringProperties.h
#interface DeclaringProperties : NSObject
// ivars and {} can be omitted
#property (nonatomic, readwrite, retain) NSString *normal;
#property (nonatomic, readwrite, retain) NSString *alias;
#property (nonatomic, readonly, retain) NSString *readonly;
- (id) initWithNormal:(NSString *)aNormal alias:(NSString *)alias;
#end
DeclaringProperties.m
#import "DeclaringProperties.h"
// private interface
#interface DeclaringProperties ()
#property (nonatomic, readwrite, retain) NSString *readonly; // readwrite for self
#property (nonatomic, readwrite, retain) NSString *private;
#property (nonatomic, readwrite, retain) NSString *retain;
#end
#pragma mark -
#implementation DeclaringProperties
#synthesize normal, alias = _alias, readonly, private, retain;
// You can not use "normal" here;
// But you can still use "alias", and it is highlighted in XCode!
- (id) initWithNormal:(NSString *)aNormal alias:(NSString *)alias {
self = [super init];
if (self) {
self.normal = aNormal;
self.alias = alias;
self.readonly = #"readonly";
self.private = #"private";
// allocated(copied) variable for retained(copied) property should be released or autoreleased
NSString *alloc = [[NSString alloc] init];
self.retain = alloc;
[alloc release];
// or
self.retain = [[NSString alloc] init];
[self.retain release];
// or
self.retain = [[[NSString alloc] init] autorelease];
// I don't like ;)
retain = [[NSString alloc] init];
}
return self;
}
- (void) dealloc {
self.normal = nil;
self.alias = nil;
self.readonly = nil;
self.private = nil;
self.retain = nil;
[super dealloc];
}
#end
Given the following example
// MyClass.h
#interface MyClass {
NSMutableArray *queue;
}
#property (readonly, retain) NSArray *queue;
#end
and
// MyClass.m
#interface MyClass ()
#property (readwrite, retain) NSMutableArray *queue;
#end
#implementation MyClass
#synthesize queue;
#end
I get a Property 'queue' type in 'MyClass' class continuation does not match class 'MyClass' property warning from the compiler. What is the best way to add "private" covariant setters to a class without writing them by hand?
You did it correctly. The issue here is NSArray isn't an NSMutableArray. If you make the private property a NSArray as well, and then write your own setter that accepts a NSMutableArray it should work as expected.