美文网首页
建议25:透彻了解属性的里里外外

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

作者: 花生儿 | 来源:发表于2016-04-25 13:08 被阅读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)利用类扩展可实现对属性的“篡改”。

    相关文章

      网友评论

          本文标题:建议25:透彻了解属性的里里外外

          本文链接:https://www.haomeiwen.com/subject/xuzfrttx.html