建议25:透彻了解属性的里里外外

2016-04-25  本文已影响33人  花生儿

属性,是类或对象的一个重要的特性,是面向对象封装的一个重要的提现。
1.大多数的属性受实例变量所支持
默认情况下,读写属性是受实例变量所支持的,将再次由编译器自动合成。实例变量是一个变量,且在对象的生命周期内,它存在和持有的值。实例变量在首次创建的时候被分配给内存,并在对象是否放的时候被释放。

除非另行指定,否则,合成的实例变量几乎具有与属性相同的名称,但唯一的差别是,合成的实例变量有一个下划线前缀。例如,对于属性称的fistName,合成的实例变量将成为_firstName。

虽然,对象访问它自己的最佳方法是使用属性访问器方法或点方法。但是在类实现里,更直接的方式是从任何实例方法来访问实例变量。下划线前缀,可使人清楚知道要访问的实例变量而不是一个本地变量,例如:

- (void)someMethod{
   NSString * myString = @"An intersting string";
   _someString = myString;
}

在这个例子中,很明显,myString 是一个局部变量,_someString是实例变量。

在一般情况下,应该使用访问器方法或属性来访问点语法,即使访问的是一个对象的属性;即使在自己的实现内来访问点语法,在这种情况下,最好的方式是使用self.像这样:

- (void)someMethod {
    NSString * myString = @"An intersting string";
    self.someString = myString;
    [self setSomeString:myString];
}

此规则的例外情况是:当写初始化,释放或自定义访问器方法时。

(1)可以自定义合成实例变量名称。正如前面提到的,默认情况下,可写属性使用的实例变量被称为_propertyName。如果想要使用的实例变量不同,需要在实现中指示编译器合成的变量,使用下面的语法。

@implementation YourClass
@synthesize propertyName = instanceVariableName;
......
@end

例如:

@synthesize firstName = ivar_firstName;

在这种情况下,虽然该属性仍将被称作firstName,并且通过firstName和setFirstName:存取方法或点语法可访问该属性,但是它受一个名为ivar_firstName的实例变量所支持。

(2)无需属性也可以定义实例变量。在需要跟踪的值得对象或另一个对象上的任何时候,最佳的做法是使用对象上的属性。如果需要定义自己的实例变量,而无需声明一个属性,可以在类接口或实现的顶部将他们添加到括号内,像这样:

@interface SomeClass : NSObject {
       NSString * _myNonPropertyInstanceVariable;
}
……
@end 
@implementation SomeClass {
      NSString * _anotherCustomInstanceVariable;
}

2.属性有静态动态之分
oc2.0中增加了一个新的关键字@dynamic,用于定义动态属性。所谓动态属性响度与synthesize,不是由编译器自动生成setter或getter,也不是由开发者自己写的setter或getter,而是在运行时动态添加的setter和getter。

一般定义一个属性,都使用类似如下所示的代码。

@interface Car:NSObject;
@property (retain) NSString * name;
@end
@implement : Car;
@synthesize name;
@end

这种情况下,@synthesize关键字告诉编译器自动实现setter和getter。另外,如果不使用@synthesize,也可以自己实现getter和setter,例如下面所示的代码。

@implement Car;
- (NSString *) name {
     return _name;
}
- (void) setName:(NSString *)n{
     _name = n;
}

现在通过@dynamic,还可以用第三种方法来实现name的setter 和 getter。实现动态属性,需要在代码中覆盖resolveInstanceMethod来动态添加name的setter和getter。这个方法在每次找不到方法实现的时候会被调用。事实上,NSObject的默认实现就是抛出异常。

下面是定义动态属性和实现动态属性的代码。
Car头文件Car.h的代码:

@interface Car :NSObject
@property (retain) NSString * name;
@end

3.类的属性可以被"篡改"

在oc有属性(Property)之前,在"私有"的成员变量前面必须要使用"_"前缀。后来有了属性(Property)之后,如果一个成员变量可以被其他的类访问,那就应该用属性(Property)。因此,大家养成了一个认识习惯,即属性是“一成不变,不可篡改”。一提到习惯,会马上再大脑中显示出这样的经典代码:

@interface MyClass : NSObject
@property (nonatomic ,assign) NSInterger correctCount;
@property (nonatomic,readonly,strong)NSString * quote;
- (id) initWithQuiz:(NSString *)plistName;
- (void) nextQuestion: (NSString *NSUInteger) idx;
- (BOOL) checkQuestion:(NSUinteger) question forAnswer: (NSUInteger) answer;
@end

对于如何把父类的属性correctCount 和 quote进行篡改,估计很多人都没有用过,也可能没有见过。有的人可能会这样做,在属性(Property)之前加"private",即:

@private property (nonatomic,assign) NSInteger correctCount;
@private property (nonatimic,readonly,strong)NSString * quote;

或者

@private @property (nonatomic,assign) NSInteger correctCount;
@private @property (nonatomic,readonly,strong) NSString * quote;

修改成上面两种方式进行编译,看是否可以通过?这样的写法是不合法的。那如何实现对父类的属性进行“篡改”?可以运用oc中的类扩展(Extensions)。

对于上面类的属性correntCount和quote进行“篡改”,可以用类扩展来实现:

@interface Quiz()
      @property (nonatomic,assign) NSInteger correctCount;
      @property (nonatomic,strong) NSString * quote;
@end

这样在本地内部能够读取和写入这些属性。但是在外,这些属性仍然出现只读,这样就会形成一个属性“里外不一样”。

要点#######

(1)属性的动态性定义,需要用关键字@dynamic。属性动态性是相对于@synthesis来说的,不是由编译器自动生成setter或getter,而是在运行时动态添加的setter和getter。
(2)属性采用动态性,与采用静态性相比,可以简化代码的编写,便于代码的管理。
(3)默认情况下,可写属性使用的实例变量被称为_propertyName.如果使用的实例变量不同命,需要在实现中指示编译器合成的变量。
(4)利用类扩展可实现对属性的“篡改”。

上一篇下一篇

猜你喜欢

热点阅读