iOS开发中@property引伸的各种问题

本文详细探讨了iOS开发中的@property特性,从@property的本质出发,介绍了setter和getter方法、实例变量、自动合成与动态合成、重写setter和getter的注意事项、修改实例变量名称等。进一步讨论了@property修饰符,包括atomic的线程安全性、weak和assign的区别以及堆栈概念。最后,文章深入讲解了copy、strong、mutableCopy的区别和用法,以及自定义对象如何支持copy方法。
摘要由CSDN通过智能技术生成

@property介绍

相信做过iOS开发的同学都使用过@property,@property翻译过来是属性。在定义一个类时,常常会有多个@property,有了@property,我们可以用来保存类的一些信息或者状态。比如定义一个Student类:

@interface Student : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *sex;

@end

Student类中有两个属性,分别是name和sex。

在程序中使用时,可以使用

self.name = @"xxx";
self.sex = @"xxx";

那么,为什么可以这样用呢?self.name是self的name变量嘛?还是其他的什么?属性中的copy代表什么?nonatomic呢?下面来看一下这些问题的答案。

@property 本质

@property到底是什么呢?实际上@property = 实例变量 + get方法 + set方法。也就是说属性

@property (nonatomic, copy) NSString *name;

代表的有实例变量,get方法和set方法。如果大家用过Java,相信对set方法和get方法应该很熟悉,这里的set、get方法和Java里面的作用是一样的,get方法用来获取变量的值,set方法用来设置变量的值。使用@property生成的实例变量、get方法、set方法的命名有严格的规范,实例变量的名称、get方法名、set方法名稍后再介绍。

这里需要注意的是,包括实例变量、get方法和set方法,不会真的出现在我们的编辑器里面,使用属性生成的实例变量、get方法、set方法是在编译过程中生成的。下面介绍一下set方法、get方法以及自动生成的实例变量。

setter方法

set方法也可以称为setter方法,之后看到setter方法直接理解成set方法即可。同理,get方法也被称为getter方法。

还是以上面的属性:

@property (nonatomic, copy) NSString *name;

为例,属性name生成的setter方法是

- (void)setName:(NSString *)name;

该命名方法是固定的,是约定成束的。如果属性名是firstName,那么setter方法是:

- (void)setFirstName:(NSString *)firstName;

项目中,很多时候会有重写setter方法的需求,只要重写对应的方法即可。比如说重写name属性的setter方法:

- (void)setName:(NSString *)name
{
    NSLog(@"rewrite setter");
    _name = name;
}

关于_name是什么,后续会介绍。

getter方法

以属性

@property (nonatomic, copy) NSString *name;

为例,编译器自动生成的getter方法是

- (NSString *)name;

getter方法的命名也是固定的。如果属性名是firstName,那么getter方法是:

- (NSString *)firstName;

重写getter方法:

- (NSString *)name
{
    NSLog(@"rewrite getter");
    return _name;
}

如果我们定义了name属性,并且按照上面所述,重写了getter方法和setter方法,Xcode会提示如下的错误:

Use of undeclared identifier '_name'; did you mean 'name'?

稍后我们再解释为何会有该错误,以及如何解决。先来看一下_name到底是什么。

实例变量

既然@property = 实例变量 + getter + setter,那么属性所生成的实例变量名是什么呢?根据上面的例子,也很容易猜到,项目中也经常使用,实例变量的名称就是_name。实例变量的命名也是有固定格式的,下划线+属性名。如果属性是@property firstName,那么生成的实例变量就是_firstName。这也是为何我们在setter方法和getter方法,以及其他的方法中可以使用_name的原因。

这里再提一下,无论是实例变量,还是setter、getter方法,命名都是有严格规范的。正是因为有了这种规范,编译器才能够自动生成方法,这也要求我们在项目中,对变量的命名,方法的命名遵循一定的规范。

自动合成

定义一个@property,在编译期间,编译器会生成实例变量、getter方法、setter方法,这些方法、变量是通过自动合成(autosynthesize)的方式生成并添加到类中。实际上,一个类经过编译后,会生成变量列表ivar_list,方法列表method_list,每添加一个属性,在变量列表ivar_list会添加对应的变量,如_name,方法列表method_list中会添加对应的setter方法和getter方法。

动态合成

既然有自动合成,那么相对应的就要有非自动合成,非自动合成又称为动态合成。定义一个属性,默认是自动合成的,默认会生成getter方法和setter方法,这也是为何我们可以直接使用self.属性名的原因。实际上,自动合成对应的代码是:

@synthesize name = _name;

这行代码是编译器自动生成的,无需我们来写。相应的,如果我们想要动态合成,需要自己写如下代码:

@dynamic sex;

这样代码就告诉编译器,sex属性的变量名、getter方法、setter方法由开发者自己来添加,编译器无需处理。

那么这样写和自动合成有什么区别呢?来看下面的代码:

Student *stu = [[Student alloc] init];
stu.sex = @"male";

编译,不会有任何问题。运行,也没问题。但是当代码执行到这一行的时候,程序崩溃了,崩溃信息是:

[Student setSex:]: unrecognized selector sent to instance 0x60000217f1a0

即:Student没有setSex方法,没有属性sex的setter方法。这就是动态合成和自动合成的区别。动态合成,需要开发者自己来写属性的setter方法和getter方法。添加上setter方法:

- (void)setSex:(NSString *)sex
{
    _sex = sex;
}

由于使用@dynamic,编译器不会自动生成变量,因此除此之外,还需要手动定义_sex变量,如下࿱

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值