第7章 属性声明

第7章 属性声明

OC 2.0支持使用简洁代码来调用访问方法,也允许自动生成访问方法。通过这些方便的功能,即使不手动实现访问方法,也能够操作对象的属性值。

7.1 属性是什么

7.1.1    使用属性编程

对象的实例变量一般被称为属性,状态。

属性声明是一种声明变量为属性的语法,同时引入了更简单的访问属性的方法。

对象的属性值都保存在实例变量中,可以直接访问或通过方法访问。也就是说,声明了实例变量或定义了访问方法就相当于定义了属性。

随着属性声明的引入,OC的编程风格也有了很大的变化

自动生成访问方法,能够为指定的实例变量自动生成访问方法。生成getter和setter方法。

自动生成实例变量

如果不存在同名的实例变量,在生成访问方法的同时,也会生成实例变量。

更简单地调用访问方法

可以通过点操作符(.)来调用访问方法,无论是setter方法还是getter方法都可以通过点操作符来调用。

同时点操作符也不仅限于通过属性声明生成的getter/setter方法,只要定义了访问方法(包括手动),就可以使用点操作符调用。总结起来就是访问方法可以被点操作符调用。

属性的内省

通过内省可以动态查询类中声明的属性以及属性的名称和类型

7.1.2    属性的概念

从KVC(key-value coding)之后,属性就被赋予了“从外部可以访问的对象的属性”。

OC2.0 引入了属性声明和点操作符来调用访问方法的手法。使用属性声明可以更简洁的实现访问方法。另一方面,不仅仅是访问方法,KVC中所有定义的实例变量都可以被当做属性处理。相比较而言,KVC的属性是一种更广泛的概念。

内省:说明数据的形式和内容的数据叫做元数据(meta data),说明信息的信息被叫做元信息(mate information)。例如,一个文件的原数据是:文件名为:会议记录“;生成时间为昨天16:15;文件格式为:HTML;文件编码为Unicode;只读。

一个类包含的方法和属性的相关信息也可以被看成为元信息。在面向对象的语言中,通过程序动态访问这些元信息的功能叫做内省或者反射,访问某个对象是否含有某种功能就叫做元方法。

OC能够在运行时取出类和协议的相关信息,而OC2.0能够在运行时取出类属性的定义,这是OC2.0新增加的功能。

7.2 属性的声明和功能

第4张说明了访问类实例变量中增加访问方法的重要性,为了封装,可并不是所有实例变量都需要增加访问方法,OC2.0中新增加的属性声明的功能,这个功能可以让编译器自动声明与实例变量同名的方法,省去对实例变量方法的getter/setter方法的定义。

#import

@interface Creature:NSObject{

NSString *name;

int hitpoint;

int magicpoint;

}

- (id)initWithName:(NSString *)str;

- (NSString *)name;

- (int)hitpoint;

- (void)sethitpoint;

- (int)level;

@end//


@property int hitpoint ;

属性声明功能等于直接声明了getter和setter二个访问方法、

属性声明时还可以设置属性的自定义选项,只想声明只读的访问方法(readonly)。

@property 和是否声明了实例变量无关。level 也可以用 @property 实现

7.2.2 属性的实现

@synthesize hitPoint 

通过@synthesize 编译器功能会让编译器自动为类的实例变量生成自动访问方法。

属性声明@property加上@synthesize 完成了属性的声明和实现、

@synthesize也可以为readonly生成访问方法getter。

这种情况下,属性名和类型一定要和声明一致,比如在level中没有为level属性进行@synthesize。而是手动定义了getter方法。

我们可以通过@dynamic告诉编译器自动合成无效,用户会自己手写getter和setter方法。

有时候你需要让方法和实例变量的名称不同,这时候需要将不同名的方法绑定到实例变量上。

@synthesize value = runningAverage

如上语句生成了vaule的访问方法,并将它绑定到了实例变量runningAverage上

我们可以在类的实现部分中声明一部分实例变量或全部实例变量,和第4章一样。

7.2.4 通过@synthesize生成实例变量

属性声明的变量在接口文件和实现文件都没声明的情况下,通过使用@syntehesize,就可以完成在类的实现文件中生成同名同类的实例变量,缩小了任务。

7.2.5 给属性指定选项

用@property 声明的时候可以给属性指定readonly选项,而除了readonly之外,还有一些其他选项,也可以给一个变量指定多个选项,选项之间逗号隔开。

指定方法名 getter = getter方法名  setter = setter方法名   显示指定getter方法和setter方法的名字。

读写属性 readonly ,readwrite                            只读,只写。

赋值时的选项        assign 单纯赋值    retain 进行保持操作

                              unsafe_unretained    同assign一样(ARC下的操作)

                              strong (同retain一样 ,ARC下)

                              weak 弱引用(用于ARC)

                               copy(复制对象)

 原子性操作            nonatomic (非原子性操作。非线程安全)

也可以不使用的默认访问方法名,而通过setter option来指定访问属性用的方法名。例如,我们可以通过下面这行语句来指定实例变量hitpoint和hitpoint的setter方法为setValue:,

@property (setter = setValue:) int hitpoint;

7.2.6 赋值时的选项

我们可以为可读写的@property 设置选项,共有6种选项:assign , retain, unsafe_unretained,strong,weak.copy. 选项之间是排他关系,只能设置其他中的一种或者不设置,根据所修饰的属性是否为对象类型或者采取内存管理方式的不同,选项的意义也会发生变化

unsafe_unretained 和 strong 主要被使用在ARC的情况下,分别和assign 和 retain 具备一样的功能


总结

(1)@property的属性不是对象类型

不是对象类型的属性只可以单纯赋值,因此不需要指定任何选项,或者也可以指定assign选项,通过使用@synthesize,能够生成getter和setter方法。

- (TYPE)name{                                                                      - (void)setName:(TYPE)obj{

   return name;                                                                                     name = obj;

}        a                                                                                            }        b

(2)@property的属性是对象类型,且手动管理内存

不指定任何选项的情况下,编译的时候会告警。指定了assign选项(赋值)的情况下,通过@synthesize会生成和(1)一样的getter、setter方法

 指定了retain(保持)选项,会生成   c 的setter方法,在赋值时应该对该对象进行保持操作

-     (void) setName:(Type)obj{                                         

        if(name != obj){

            [name release];

            name = [obj retain];

        }

}                     C

指定了copy选项的情况下,会生成d的setter方法,并使用对象的一个副本来进行赋值,就是不使用输入的对象对属性进行赋值,而是生成一个副本进行属性赋值,这种赋值方式只使用于对象类型,并且要求该对象遵循NSCopying协议,且能够使用copy方法。

- (void)setName:(TYPE)obj{

            if(name != obj){

                [name realse];垃圾回收的情况下没有意义

                name = [obj copy]

            }

}                D

(4) 属性是对象类型,且使用垃圾回收管理内存

这种情况下,如果不指定任何选项或指定了assign选项,@synthesize会生成a,b的getter,setter方法。

但要注意,只有符合NSCopying协议可以利用copy方法的类实例变量,如果不指定任何选项,则会告警。

选项retain 和 weak 没有意义,指定了也会被忽略,并且执行的也是assign选项,实例变量则需要用_ _ weak选项修饰。

@property (assign) _ _ weak NSString *nickname;

指定copy后,会生成 d中的setter方法、


7.2.7 原子性

nonatomic这个选项是在多线程环境下使用的,nonatomic表示访问方式是非原子的,原子性是多线程的一个概念,如果说访问方法是原子的,那么就意味着多线程环境下访问属性是安全的,在执行的过程中不可能被打断。缺省的情况下访问方法是原子的。

c和d都是指定了nonatomic选项的实现。当属性为对象类型时,使用了retain并且没有指定nonatomic时,getter和setter的实现如下所示,没指定nonatomic,访问方法中需要使用lock和unlock来保证方法的原子性。

- (TYPE)name    {                                                                - (void)setName:(TYPE)obj{

       [_ex lock]; //    局部锁                                                            [_ex lock];//局部锁

       TYPE rth = [[name retain]autorelease];                                if(name != obj){      

       [_ex unlock];                                                                           [ name release];      

       return rtn;                                                                                name = [obj retain];

}                                                                                                       }

                                                                                                      [_ex unlock]  ;   

                                                                                                        }   b

还有一些没说


7.2.8 属性声明和继承

子类可以使用父类中定义的属性,也可以重写父类中定义的访问方法。但是,父类中属性声明时指定的各种属性(assign,retain),或者为实例变量指定的getter和setter的名称等必须完全一样。

唯一一个特别的情况是,对于父类中被定义为readonly类型的属性,子类中可以将其变为readwrite。虽然不可以在子类中使用@synthesize 对父类中的实例变量生成访问方法,但可以手动实现对应方法,这是为了防止子类轻易访问父类中隐藏的实例变量。

属性的声明可能会包含范畴或协议。这种情况下实现文件中不可以使用@synthesize,原理是范畴和协议都和实例变量的实现无关,需要在实现文件中实现访问方法。

7.2.9 方法族和属性的关系

使用ARC的时候,必须注意方法的命名,不要和方法族发生冲突。

属性声明的时候回默认生成和属性同名的getter访问方法,需要注意属性名是否和方法名冲突,特别是new开头的属性名。

7.3 通过点操作符访问属性

OC 2.0 中新增了使用点操作符来访问属性的功能

上面说过在key-value coding 中属性被赋予了从外部可以访问的对象属性

访问操作符是点操作符、

OC 2.0 会在编译时把使用点操作符访问属性的过程理解为访问方法的调用,因为调用的是访问方法,所以无论对应的实例变量是否存在,只要访问方法存在,就可以通过点操作符访问属性。

点操作符只能用于类类型的实例变量,不能对id类型的变量应用点操作符。在动态类型下,编译器无法判断是否存在属性对应的访问方法。


        操作                                使用点操作符                            使用消息表达式

        赋值(setter)                obj.name = val;                            [obj     setName:val];            

        获取(getter)                val = obj.name;                            val = [obj name];    


7.3.2 复杂的点操作符的使用方法

(1)连用点操作符

n    =    obj.productList.length;

obj.contents.enable    =    YES;

点操作符按照从左向右的顺序进行解释,所以上面的表达式可被解释为

n    =    [    [obj    productList]    length];

[[obj.contents]    setEnabled:YES];

(2)连续赋值

n    =    0;

k    =    obj.count    =    obj.depth    =    ++n;

k    =    (obj.count = (obj.depth    =    ++n))

点操作符与C语言点宏定义不同,不会对n重复+1;


7.3.3    何时使用点操作符

没有参数的方法,无论他是否与属性相关,都可以使用点操作符来调用,其机制和getter方法是一致的

OC中使用点操作符会带来调用访问方法的负担,所以在严格要求性能的条件下,使用点操作符并不是一个很好的选择。

在其他语言中,通过点操作符调用对象的方法。但对OC来说,使用点操作符的目的只是访问属性。

你可能感兴趣的:(第7章 属性声明)