Objective-C, interface declarations with properties
In the following common sample,
////
@interface MyObject : NSObject
{
@public
NSString * myString_;
}
@property (assign) NSString * myString;
@end
@implementation MyObject
@synthesize myString开发者_如何转开发 = myString_;
@end
////
why declare myString_
in the interface at all?
I ask because we can still get and set myString
in the implementation using self.myString
, [self myString]
, self.myString = ...
and [self setMyString:...]
and in fact we must if instead it's being retained.
This is a matter of preference/convention for some. By default, doing:
@property (assign) NSString * myString;
...followed by:
@synthesize myString;
...will give you three things. You get a setter method that can be accessed as self.myString = @"newValue"
or [self setMyString:@"newValue"]
, a getter method that can be accessed as NSString* temp = self.myString
or NSString* temp = [self myString]
, and an instance variable named myString
that be be accessed directly inside of your class (i.e. without going through the getter and setter) and used to set and get the property value, and which is used internally to back the property.
If you like you can do @synthesize myString = someOtherVarName
, and then you still get the setters and getters just as before, but instead of the myString
instance variable the someOtherVarName
instance variable is used to back the property, and no myString
variable is created.
So why ever use the more verbose syntax? There is never any case that requires that you do so, but some people prefer to do so when dealing with properties that are declared retain
or copy
. The reason for this being that setting a property declared retain
or copy
via its generated setter method will affect the retain-count of the object being set/unset. Doing the same thing by accessing the instance variable directly will not.
So by aliasing the instance variable to something else, you can make a distinction in the code along the lines of "anything that does xxx.myString = Y
is modifying the retain count, while anything that does someOtherVarName = Y
is not". Again, it's not necessary to do this, but some people prefer to.
You should be able to skip it. Modern compilers allow that.
When you define a property, you are actually declaring how the getter and setter methods are constructed for a particular instance variable. Earlier it needed the instance variable to be defined so you declared it. It also allowed the property name to differ from the instance variable name via @synthesize myProperty = myIVar;
. Now you don't need to do this as the modern compilers generate the instance variable for you.
The dot syntax is actually a convenience thing as you would've noticed. It doesn't directly refer to the instance variable but the methods myProperty
and setMyProperty:
. You can even call myArray.count
where count
isn't a property (I wouldn't recommend it even though lot of people seem to like it).
While there is a difference between the two, the gap seems to be slowly closing.
That's just a problem about point of view. If you access ivar directly, it's you're accessing it internally. If you're using property, you're not accessing ivar (semantically). You're using accessing method of the object. So you're handling the self
as like external object which the internal is unknown.
This is encapsulation problem of Object-Oriented paradigm.
And I recommend some tricks when using properties.
- The ivar declaration is optional, not required. Compiler will generate it automatically.
- You should set the ivar as
@protected
or@private
to encapsulate it correctly. (at least there is no reasonable reason) - I recommend to use
nonatomic
if you don't need threading lock when accessing the property. Threading lock will decrease performance greatly, and may cause strange behavior in concurrent execution code.
You can use this code to do same thing.
@interface MyObject : NSObject
@property (assign,nonatomic) NSString * myString;
@end
@implementation MyObject
@synthesize myString;
@end
And this will be transformed roughly something like this.
@interface MyObject : NSObject
{
@private
NSString* myString; // Ivar generated automatically by compiler
}
@end
@implementation MyObject
// Methods with thread synchronization locking generated automatically by compiler.
- (NSString*)myString { @synchronized(self) { return myString; } }
- (void)setMyString:(NSString*)newMyString { @synchronized(self){ myString = newMyString; } }
@end
In fact, I'm not sure about synchronization lock with assign
behavior directive, but it's always better setting it nonatomic
explicitly. Compiler may optimize it with atomic operation instruction instead of locking.
Here is reference document about the properties: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProperties.html%23//apple_ref/doc/uid/TP30001163-CH17
With the modern Obj-C runtime, declaring the ivar is more of a formality than anything else. However, there are some memory management things to keep in mind.
First, the property declaration for an object type is usually retain
, or for strings it may be copy
. In either case, the new object is retained.
Given the following code:
NSString *string = [[NSString alloc] init];
myString_ = string;
self.myString = string; // If the property was retain or copy
The second assignment would leak; the first would not. This is because the property would retain something that already has a retain count of 1—it is now at 2. When you release the property in dealloc
, the count goes to 1, not 0, so it won't be released. With the first option, however, the retain count stays at 1, so dealloc
brings it down to 0.
In your example, leaving the property as assign
will make the ivar declaration a formality.
精彩评论