OC:属性

目录

  • 属性演变过程
  • 属性定义
  • 属性扩展

属性演变过程

上篇博文《OC学习备忘录:成员变量、方法》中演示了如何声明成员变量,并在方法中使用。
这里继续以WZKPerson类为例,上篇博文中我们声明了一个lisi的对象,并初始化了该对象的name和age两个成员变量。

WZKPerson *lisi=[[WZKPerson alloc]initWithName:@"李四" age:30];
[lisi sayMyInfo];

假如我们想给lisi进行改名字,该怎么办呢?
由于成员变量在类的内部,无法直接进行修改,又该怎么办呢?
一种简单的办法是,可以利用对象方法进行修改成员变量。
首先需要在WZKPerson.h文件中添加如下方法:

-(void)setName:(NSString *)name;
-(void)setAge:(NSInteger)age;

对应的需要在.m文件中实现这两个方法:

-(void)setName:(NSString *)name
{
   _name=name;
}
-(void)setAge:(NSInteger)age
{
    _age=age;
}

最后就可以在调用类中使用这两个方法了。

//设置lisi的name为李斯
[lisi setName:@"李斯"];
//设置lisi的age为90
[lisi setAge:90];
//输出设置name和age信息
[lisi sayMyInfo];

运行效果如下:



第二个运行结果是没有改变lisi的name和age之前的结果,第三个结果是改变之后的运行结果。

通过方法修改成员变量的值还是比较简单的,那么问题又来了,现在我们想获取lisi的name和age值,又该怎么办呢?同样的方式,可以写一个新的方法进行获取。

在WZKPerson.h文件中添加如下方法:

-(NSString *)name;
-(NSInteger)age;

对应的需要在.m文件中实现这两个方法:

-(NSString *)name
{
    return _name;
}

-(NSInteger)age
{
    return _age;
}

最后就可以在调用类中使用这两个方法了。

NSString *lisiName=[lisi name];
NSInteger lisiAge=[lisi age];
NSLog(@"lisiName= %@ , lisiAge= %li",lisiName,lisiAge);

运行效果如下:


这种通过定义方法的方式访问类的成员变量,其实就是利用到了面向对象中封装特性

到现在为止,通过定义方法的方法的方式设置和获取成员变量的值看起来没有什么问题,但是会引出一个问题。
假如又定义了一个NSMutableString类型的变量personName,将personName值赋值给wangwu的name变量。

NSMutableString *personName=[NSMutableString stringWithString:@"王五"];
[wangwu setName:personName];
NSLog(@"personName= %@,wangwu's name=%@",personName,[wangwu name]);

输出结果肯定是:“personName= 王五,wangwu's name=王五”。
现在我希望personName值为“王五123”,wangwu 的name值还是为“王五”。可能我们会用下面代码来改变personName的值。

[personName appendString:@"123"];

添加完这行代码后,重新运行一下,发现输出的结果为:“personName= 王五123,wangwu's name=王五123”,和我们预期的结果不一样,这是什么原因造成呢?
还记得在WZKPerson.m文件中的这两段代码吗?

-(id)initWithName:(NSString *)name age:(NSInteger)age
{
    self=[super init];
    if (self) {
        _name=name;
        _age=age;
    }
    return  self;
}
-(void)setName:(NSString *)name
{
    _name=name;
}

造成的原因是因为_name=name导致的,这又是为什么呢?
这里先来看一下personName和[wangwu name]两个值的内存地址。

NSLog(@"%p",personName);
NSLog(@"%p",[wangwu name]);

运行后,发现personName内存地址为0x100300aa0,另外[wangwu name]也是0x100300aa0,这两个内存地址一样,说明通过_name=name赋值方式,[wangwu name]的内存地址指向了personName的内存地址,这种方式叫做浅拷贝。

终于找到原因了,那有没有解决办法来实现我们的预期结果呢?
当前可以了,既然有浅拷贝,那必然会有一个叫深拷贝(copy)的东东。那么怎么使用copy呢?很简单,直接看代码。

-(id)initWithName:(NSString *)name age:(NSInteger)age
{
    self=[super init];
    if (self) {
        _name=[name copy];
        _age=age;
    }
    return  self;
}
-(void)setName:(NSString *)name
{
    _name=[name copy];
}

只需要将原来的_name=name代码改成_name=[name copy]就可以了。
重新运行一下,发现输出的结果变为:“personName= 王五123,wangwu's name=王五”,达到了预期结果。
再来看一下内存地址,验证一下是不是指向两个不同的内存地址。
发现personName内存地址变成了0x1003004d0,而[wangwu name]内存地址变成了0x100300a90。
内存地址也不一样,完全达到了预期结果。

age成员变量由于是NSInteger类型,不会涉及深拷贝和浅拷贝

但是问题又来了,我们发现,仅仅两个简单成员变量的设置和获取操作,就需要写十几行的代码,如果一个类里面有十几个变量,那么仅仅这一块代码就需要上百行,严重影响了开发效率,并且错误率也会大大的提升。

属性定义

苹果公司在Objective-C 2.0中引入了属性(property),它组合了新的预编译指令和新的属性访问器语法。新的属性功能显著的减少了冗余代码的数量。

怎么在上述代码中添加WZKPerson的name和age的属性的呢?

首先我们先将WZKPerson类.h和.m文件中:
-setName:、-setAge:、-name:和-age:方法的声明和实现注释掉。

在WZKPerson.h文件添加如下代码。

@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;

最后运行一下程序,发现输出的结果和之前是一样的。


另外也可以通过点方式设置或者获取属性值。

wangwu.name=@"12345";//等价于[wangwu setName:@"12345"];
wangwu.age=20;//等价于[wangwu setAge:20];
[wangwu sayMyInfo];

到此为止,我们就成功的添加了WZKPerson类的name和age的属性,是不是很简单。

下面稍微解释属性声明的代码:

  • OC中可以通过@property声明属性;
  • 通过@synthesize自动生成getter、setter方法(目前可以省略@synthesize声明);

属性声明后,甚至也可以直接省略掉成员变量的声明

下面总结一下属性的生成规则:

  1. 如果只声明了一个属性a,不使用@synthesize实现,编译器会使用_a作为属性的成员变量。
    如果没有定义成员变量_a,则会自动生成一个私有的成员变量_a;
    如果已经定义了成员变量_a则使用自定义的成员变量_a;
    注意,如果此时定义的成员变量不是_a而是a,则此时会自动生成一个成员变量_a,它和自定义成员变量a没有任何关系;
  2. 如果声明了一个属性a,使用@synthesize a进行实现,但是实现过程中没有指定使用的成员变量,则此时编译器会使用a作为属性的成员变量。
    如果定义了成员变量a,则使用自定义成员变量;
    如果此时没有定义,则自动生成一个私有的成员变量a;
    注意,如果此时定义的是_a,则它跟生成的a成员变量没有任何关系;
  3. 如果声明了一个属性a,使用@synthesize a=_a进行实现,这个过程已经指定了使用的成员变量,此时会使用指定的成员变量作为属性变量;

属性扩展

可能大家会发现,在上面声明属性代码中有一个()号,并且其中有一些参数。

@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;

()号中的参数,我们叫做属性限定词。
这些限定词包括:nonatomic、atomic、copy、retain、strong、weak、assign、 readonly、readwrite;
下面简单的介绍这些限定词的含义:

限定词 说明
atomic 默认值,表示原子,同一时间可以由多个线程访问属性
nonatomic 表示非原子,同一时间只能有一个线程进行访问
readonly 只读属性,只生成getter方法
readwrite 默认值,生成getter、setter方法
assign 默认值,直接赋值,通常修饰基本数据类型,也可以修饰对象数据类型,但此时会指向同一个内存地址
retain 先release原来的值,再retain新值,会导致引用计数+1
copy 先release原来的值,再copy新值(深拷贝)
strong iOS4.0之后出来的关键字,会导致引用计数+1,作用等价于retain
weak iOS4.0之后出来的关键字,用于对象数据类型,作用等价于assign

@property的参数最多可以有三个,中间用逗号分割。如果不进行设置,程序会使用默认参数:(atomic,readwrite,assign)

一般情况下,如果在多线程开发中一个属性可能会被多个线程同时访问,可以考虑用atomic,否则建议使用nonatomic,效率更高;

小技巧:通常情况下,字符串对象使用copy,非字符串对象使用retain,基本数据对象使用assign;
另外使用copy功能需要一些前提,需要遵守NSCopying协议,实现copyWithZone:方法;

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

推荐阅读更多精彩内容

  • @property 功能: 给现有的成员变量生成一对setter、getter方法 如果没有声明成员变量时自动生成...
    李琪_59dc阅读 4,447评论 0 4
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,292评论 18 399
  • 1. 属性 属性是 objective-c2.0 定义的语法, 为实例变量提供了setter和getter方法的默...
    李小六_阅读 1,640评论 1 4
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,087评论 18 139
  • OC属性 [toc] 属性的作用 1. 为成员变量提供访问的接口。setter和 getter 方法 @prope...
    One1丨光阅读 1,344评论 0 4