属性
属性:是OC的一项特性,用于封装对象中的数据。
- iOS中通常用@property的形式来声明属性,一般为我们认为属性 = setter+ getter + 实例变量
- 其实@property是一种语法糖,编译器会自动为你的实例变量生成setter和getter方法。你在获取这个属性值(NSLog(@"%@",self.name);)和设置属性值(self.name = @“dasheng”)的时候其实是调用了getter和setter方法获取和设置实例值。编译器帮你生成的实例变量就是你的属性名前面加个下划线。
补充:get和set方法的调用
- 一般的调用方法,是传统的带中括号[ ]的调用方法
//比如上面的声明是一个Person类
Person* person=[[Person alloc]init];
[person setAge:13];
int age=[person age];
- 点语法的方式
//点调用
person.age=13; //.调用出现在=号左边,相当于setter
int age=person.age //.调用出现在=号的右边,相当于getter
NSLog(@"%i",person.age);//这也是getter
关于get和set的注意点
- 属性的setter方法和getter方法是不能同时进行重写的
-
在getter方法中最后返回
return _age;
而不是return self.age
, 这是因为点语法实际上是对setter和getter方法的调用,如果在getter
方法中调用return self.age
的话,就会循环调用。 -
声明set方法和get方法的规范
a. set方法
- 返回值类型必须是(void)。
- 方法名以set开头,set后面跟上成员变量名(去掉下划线)且成员变量名首字母大写。
- 形参名不能和成员变量名相同
- 接收一个参数,参数的类型与成员变量类型一致。
b. get方法
- 返回值类型与成员变量类型一致。
- 方法名与成员变量名(去掉下划线)相同。
- 不需要接收参数
属性关键字和所有权修饰符不一样
@property/@synthesize/@dynamic语法
-
@property
刚刚我们有讲iOS中通过@property来自动生成setter和getter方法以及实例变量,其实准确来讲,这种说法是不对的,实际上@property只会生成setter,getter的方法声明
-
@synthesize:
@synthesize会在编译期创造一个指定的成员变量,并且生成属性的setter和getter方法的实现部分
所以在之前是@property和@synthesize搭配使用,,后来编译器可以属性自动合成,所以现在我们的代码中已经很少看到@synthesize了
然而@synthesize还有另外一个用法,我们也可以使用@synthesize来指定属性对应的实例变量名
@synthesize name = realname; //前述语法会将生成的实例变量命名为realname,而不再使用默认的名字name,一般情况下无需修改默认的实例变量名。
-
@dynamic:
它会告诉编译器,不要自动创建实现属性所用的实例变量,也不要为器创建存取方法,这就需要开发者自己来添加属性的实例变量名,动态合成实现getter和setter方法
属性关键字
属性关键字可以分为三种类型:
- 读写权限的类型: readonly ,readwrite
- 原子类 : atomic ,nonatomic
- 引用计数 : retain/strong/copy,assign/unsafe_unretained,weak
系统一般默认的关键字
- 基本数据:atomic,readweite,assign
- OC对象:atomic, readwrite,strong
属性关键字之原子类
- atomic:默认情况下,由编译器所合成的方法会通过锁定机制确保其原子(atomic)
- nonatomic: 如果属性具备nonatomic特质,则不使用同步锁。
- 一般都写nonatomic
- 有效的属性值。若是不加锁的话(即nonatomic),那么其中一个线程正在改写某属性时,另一个线程突然闯入,就有可能读到不准确的数值。
- 如果开发过iOS程序,你就会发现,所有的属性都声明为 nonatomic。这样做的历史原因是:在iOS程序中使用同步锁的开销较大,这会带来性能问题。一般情况下并不要求属性必须是“原子的”,因为这并不能保证“线程安全”,若要实现“线程安全”的操作,还需采用更为深层的锁定机制才行。例如:一个线程在连续多次读取某属性值的过程中有别的线程在改写该值,那么即便属性声明为atomic,也还是会读到不同的属性值。atomic仅保证了属性的setter/getter的线程安全,并不能保证使用属性的过程是线程安全的。因此,开发iOS程序时一般都会使用nonatomic属性。但是在开发MacOS X程序时,使用atomic属性通常时没有问题的。
属性关键字之读写权限
- readwrite:拥有“获取方法”(getter)和“设置方法”(setter)readwrite是默认属性
- readonly:仅拥有“获取方法”(getter)可以直接访问
属性关键字之引用计数
- strong
-
先保留新值,再释放旧值,然后再将新值设置上去。
在ARC下使用strong不用担心引用计数的问题,会在你不需要该对象时自动将其释放
-
我持有这个对象,在我使用完它之前,不能销毁。
-
strong可以说是ARC下retain的表现形式
- retain
- 释放旧对象,并使传入的新对象引用计数+1.
- retain只能修饰oc对象,不能修饰非oc对象,一般用来修饰非NSString 的NSObject类和其子类。
- retain在ARC环境下使用较少,在MRC下使用效果与strong一致
- assign
-
修饰对象类型时,不改变引用计数
-
修饰基本数据类型:int、bool ,NSInteger
-
不能用assign去修饰对象,可以用来修饰基本数据结构
对象的内存一般被分配到堆上,基本数据类型和oc数据类型一本被分配在栈上。
如果用assign修饰对象,当对象释放后(因为不存在强引用,离开作用域对象内存可能被回收),指针的地址还是存在的,也就是说指针并没有被置为nil,下次再访问该对象就会造成野指针异常。对象是分配在堆上的,堆上的内存由程序员手动释放。
用assign修饰基本数据类型或OC数据类型时,因为基本数据类型是分配在栈上的,由系统分配和释放,所以不会造成野指针。
- week
- 不改变被修饰对象的引用计数。
- 弱引用,指向赋值的对象,所指向的对象被释放之后会自动置为nil
- 只要对象不在被强引用,那么该对象将会被释放,同时所有的弱指针都将被置为nil。可以避免循环引用。
- 为这种属性设置新值时既不会保留新值也不会释放旧值,类似与assign
- copy
-
在iOS开发中,一般copy关键字用在NSString、NSArray、NSDictionary等属性字段的修饰符。
-
假如有一个NSMutableString,现在用他给一个retain(strong)修饰 NSString赋值,那么只是将NSString指向了NSMutableString所指向的位置,并对NSMutbaleString计数器加一,此时,如果对NSMutableString进行修改,也会导致NSString的值修改,原则上这是不允许的. 如果是copy修饰的NSString对象,在用NSMutableString给他赋值时,会进行深拷贝,及把内容也给拷贝了一份,两者指向不同的位置,即使改变了NSMutableString的值,NSString的值也不会改变.
为了安全,防止NSMutbaleString赋值给NSString时,前者修改引起后者值的变化。
深浅复制
- 深复制是内容复制,产生新对象,会产生新的内存空间
- 浅复制是指针复制,没有产生新对象,会增加引用计数
copy关键字:
- copy修饰不可变对象、原对象为不可变对象时,将原对象赋值给属性,会将原对象进行copy,此时是浅复制,两个指针指向的是同一个地址。
- copy修饰不可变对象,原对象为可变对象时,将原对象赋值给属性,会将原对象进行copy,此时是深复制,两个对象指向的不同的地址,属性所指的是可变对象的副本,原对象如果被修改的话,不会影响属性的值,这时对属性进行增删改的操作,就会因为找不到方法而报错。
@property (nonatomic, copy) NSString *strCopy;
NSString *str = @"12345";
person.strCopy = str;
NSLog(@"%p %p", str, person.strCopy);
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:@"asdgfg"];
person.strCopy = mutableString;
NSLog(@"%p %p", mutableString, person.strCopy);
[mutableString appendString:@"weret"];
NSLog(@"mutableString - %@ %p, person.strCopy - %@ %p", mutableString, mutableString, person.strCopy, person.strCopy);
2021-07-30 21:51:15.015812+0800 属性关键字[59912:2479739] 0x100002060 0x100002060
2021-07-30 21:51:15.021123+0800 属性关键字[59912:2479739] 0x100620690 0x1f30fa489daca0dd
2021-07-30 21:51:15.049619+0800 属性关键字[59912:2479739] mutableString - asdgfgweret 0x100620690, person.strCopy - asdgfg 0x1f30fa489daca0dd
strong关键字
由于strong修饰属性在设置新值时,在setter方法中保留新值、并释放旧值,将新值设置上去,此时与原对象指向的是同一地址。
当原对象为可变对象时,将原对象赋给strong修饰的不可变对象,修改原对象,那我们不可变的对象也会随之改变
@property (nonatomic, strong) NSString *strCopy;
NSString *str = @"12345";
person.strCopy = str;
NSLog(@"%p %p", str, person.strCopy);
NSMutableString *mutableString = [[NSMutableString alloc] initWithString:@"asdgfg"];
person.strCopy = mutableString;
NSLog(@"%p %p", mutableString, person.strCopy);
[mutableString appendString:@"weret"];
NSLog(@"mutableString - %@ %p, person.strCopy - %@ %p", mutableString, mutableString, person.strCopy, person.strCopy);
//结果
0x100001060 0x100001060
0x10072baf0 0x10072baf0
mutableString - asdgfgweret 0x10072baf0, person.strCopy - asdgfgweret 0x10072baf0