第七章 属性声明

使用属性编程:

一般来说,属性指的是一个对象的属性或特性。对象的实例变量,也就是访问方法的目标一般被称为属性。

以前的接口文件中使用实例变量和访问方法实现属性的概念,而现在把属性的概念作为一个独立的存在在接口中声明。

属性声明的规则总结如下:

自动生成访问方法

自动生成实例变量

更简单的调用访问方法

属性的内省

属性的概念:

使用属性声明,可以更简洁地实现访问方法。另一方面,不仅仅是访问方法,KVC中所有定义的实例变量都可以被当作属性处理。

显式声明属性:

OC2.0中新增加了属性声明的功能。这个功能可以让编译器自动生成与数据成员同名的方法。

@property int hitPoint;

属性声明等同于声明了读写两个访问方法。

属性声明的时候还可以为属性自定义选项。选项位于圆括号中,前面是@property指令。例如如果想声明一个只读的访问方法,可以在@property后面加上(readonly)。

@property(readonly)  NSString *name;

当两个属性的类型相同时,既可以单独写一行,也可以将他们写一起。例如:

@property int hitPoint, magicPoint;

属性的实现:

通过使用@synthesize,就可以在一行之内自动生成getter和setter方法。将语句应放在@implementation和@end之间,就能自动生成和接口文件中声明的属性一致的访问方法(可读可写或只读)。也可以不使用@synthesize自动生成,而是由自己来实现访问方法。另外我们还可以通过@dynamic关键字告诉编译器合成无效,用户会自己生成getter和setter。

其他方法可以直接在实现文件中实现,而不用在接口文件中声明。但是属性声明的情况下则不允许这种做法。

使用@synthesize的时候,可以在一行中声明多个变量。

通常情况下,@property声明的属性名称和实例变量的名称是相同的,但有时你也可能会需要属性的名称和实例变量的名称不同,这时就可以为实例变量定义其他的属性名称。例如我们可以通过该语句生成名为value的访问方法,并将其绑定到实例变量runningAverage中:

@synthesize value = runningAverage;

可以在类的实现部分中声明一部分或全部实例变量,这种声明方法可以隐藏是否对变量进行了属性声明。另外在子类中访问实例变量时也只能通过访问方法来访问,不能直接访问父类的实例变量。

通过属性声明的方法也能够同访问方法一样实现封装的目的。

给属性指定选项:

可以同时给一个变量指定多个选项,选项之间需要用逗号隔开。

@property 可用选项:

种类

选项

说明

指定方法名

getter = getter方法名

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

读写属性

readonly

readwrite只读

读写

赋值时的选项

assign

retain

unsafe_unretained

strong

weak

copy单纯赋值

进行保持操作

同assign一样(用于ARC)

同retain一样(用于ARC)

弱引用(用于ARC)

复制对象

原子性操作

nonatomic

非原子性操作,非线程安全

指定方法名:

可以不使用默认的访问方法名,而通过setter option来指定访问属性用的方法名。例:

@property(setter = setValue)int hitPoint;

可以通过点运算符来调用 .hitPoint,但实际上启动的方法是setValue

读写属性:

readwrite表示属性是可读写的,这也是默认选项。

赋值时的选项:

六个选项之间是排他关系。unsafe_unretained和strong主要被用在ARC的情况下,分别和assign和retain具备同样的功能。

基础数据类型

对象类型:手动引用计数

对象类型:ARC

对象类型:垃圾回收

未指定任何选项

直接赋值

警告

警告

直接赋值(有可能出现警告)

assign

unsafe_unretained直接赋值

直接赋值

直接赋值

直接赋值

retain

strong出错

赋值并对新值进行retain

赋值并对新值进行retain

无特别操作,和assign动作相同

weak

出错

无特别操作,和assign动作相同

弱引用

无特别操作,和assign动作相同

copy

出错

赋值时建立传入值的一份副本

赋值时建立传入值的一份副本

赋值时建立传入值的一份副本

属性是对象类型,且使用了垃圾回收管理内存,有一点需要注意,对于符合NSCopying协议也就是说可以利用copy方法的类实例变量,如果不指定任何选项,就会提示警告。

原子性:

原子性是多线程中的一个概念,如果说访问方法是原子的,那就意味着多线程环境下访问属性是安全的在执行过程中不可被打断。而nonatomic则正好相反,意味着方法在执行时可被打断,缺省情况下访问方法是原子的。

通常不需要指定nonatomic选项,因为这样的机制能提高访问的安全性。但毕竟lock和unlock操作对性能有影响,因此,对于使用频繁且不用考虑多线程竞争的访问方法可以在声明的时候加上nonatomic。

nonatomic选项不仅能被用于使用@synthesize生成的访问方法,手动定义的访问方法中不存在多线程竞争的情况下,也可以给属性加上nonatomic。

属性声明和继承:

子类可以使用父类中定义的属性,也可以重写父类中定义的访问方法,但是,父类中属性声明时指定的各种属性(assign等),或者为实例变量指定的getter和setter的名称等必须完全一样。唯一一个特别的情况是,对于父类中被定义为readonly类型的属性,子类中可以将其变为readwrite。

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

方法族和属性的关系:

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

点操作符的使用方法:

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

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

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

连用点操作符:

当一个对象的实例变量是另外一个对象时,可用过连用点操作符来访问对象的实例变量中的成员。如果连用表达式中有一个是nil,则整个表达式的返回值就是nil。

连续赋值:

赋值时从右向左解释。

对递增,递减和复合赋值运算符的解释:

e = obj.depth++;

赋值表达式的右侧连续调用了getter和setter方法,相当于执行了[ obj setDepth: [ obj depth ] + 1 ]。最后为e赋值的是递增操作之前的depth的值。

self使用点操作符:

类的方法中可以通过对self应用点操作符来调用自己的访问方法。但要注意的是,不要在访问方法中使用self,否则会造成无限循环的递归,无法终止。

super使用点操作符:

可以通过给super加点操作符来调用父类中定义的setter和getter方法。例如:

- (void)setDepth:(int

)val {

super

.depth = (val <= maxDepth) ? val : maxDepth;

}

和构造体成员混用:

获取类属性的点操作符和访问结构体元素的点操作符可以混用。不能通过取地址符来对点操作符获得的属性取地址。

当给obj的实例变量contents发送消息时,你可能会这样写:[ obj.contents retain ]  。但要注意的是,实际上这行语句表示的是给getter方法的返回值发送了消息,并不一定会给obj的实例变量contents发送消息。

使用点操作符访问对象的实例变量和C语言中使用点操作符访问结构体的成员意义是不一样的。访问对象的实例变量的最正统的方法是通过 -> 操作符来访问。编译器在碰到点操作符的时候并没有直接访问实例变量而是调用了访问方法。

何时使用点操作符:

没有参数的方法,无论其是不是和属性相关,都可以通过点操作符来调用。但原则上还是只对属性声明中定义的属性应用点操纵符。

使用点操作符会带来调用方法的负担,影响性能。

严格来说,使用依赖于实现的方式来访问实例变量是不允许的,所以应避免直接访问实例变量。但属性对应的访问方法则一定要直接访问实例变量。

此外,在初始化方法中通过点操作符访问属性的时候要注意,因为初始化方法执行的时候这个实例还没完成初始化,属性对应的访问方法有可能还没生成。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,652评论 18 139
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,802评论 1 10
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,201评论 9 118
  • “精致”是指生活得有情致、有趣味、美好。多与心态轻松,趣味不俗,情怀浪漫,追求诗意、情调、美好有关,与钱多钱少关系...
    青鱼吹浪阅读 1,528评论 0 4
  • 唉,人,就这样。
    小嘴欠吻阅读 45评论 0 0