iOS——属性关键字strong和copy

属性关键字

在定义属性时,经常遇到属性关键字,例如定义UI控件时,经常使用nonatomic以及strong两个属性关键字,但没有了解过他们是干什么的,今天进行了浅层次的学习。

@property

当我们写下@property NSOject *foo时,编译器会自动创建实例变量_foo,帮我们声明foo属性的setter,getter方法,这个过程是在编译阶段执行自动合成,这里生成的成员变量的名字会在属性名前加下划线,作为实例变量的名字。

一些常见的属性关键字及其作用

属性关键字大致上可分为三种,ARC环境下的关键字,MRC环境下的关键字,以及不分环境的关键字。
ARC环境:
strong,weak
MRC环境:
retain
不分环境:
copy,assign,nonatomic,atomic,readonly,readwrite,getter,setter,class,unsafe_unretained.

atomic:原子性访问。
nonatomic:非原子性访问,多线程并发访问会提高性能。
assign:不会使引用计数加1,也就是直接赋值。
copy:建立一个索引计数为1的对象,在赋值时使用传入值的一份拷贝。
strong:ARC下可使用,使引用计数加1.
retain:MRC下可以使用,使引用计数加1.
readwrite:这个关键字说明属性被当成读写的,即拥有setter,getter方法,这个属性是默认属性。
readonly:这个关键字说明属性只可读,也就是不能设置该属性,只能读取该属性。
weak:建立一个索引计数为1的对象,在赋值时使用传入值的一份拷贝。
getter,setter:可以重命名生成set get 方法名字,使用方法:

@property(nonatomic, copy, getter = string) NSString *firstString;

class:可以使属性通过类名加点语法获取(不常见)。
unsafe_unretained:与assign类似,但更加安全不会出现野指针的情况。

如何使用copy关键字

在定义NSString,NSArray等不可变NS类时,经常使用了copy关键字,这是因为他们存在可变类型NSMutableString,NSMutableArray等。
copy方法返回的都是不可变的对象,利用copy关键字可以使本对象的属性不受外界影响,不论传入的是可变对象还是不可变对象,本身持有的就是copy复制的不可变副本。

这里还涉及到了深拷贝与浅拷贝的问题

浅拷贝:对内存地址的复制,让目标对象指针和原对象指向同一片内存空间会增加引用计数
深拷贝:对对象内容的复制,开辟新的内存空间
在这里插入图片描述
由上可知可变对象的copy和mutableCopy都是深拷贝,不可变对象的copy是浅拷贝,mutableCopy是深拷贝,copy返回的都是不可变的对象。
所以可变的对象不能使用copy关键字,因为copy出的是不可变的对象,然而却把他当作可变对象来使用,调用可变对象的方法,很容易造成程序的崩溃。

自定义类使用copy关键字

若想让自定义类使用copy属性,那么就需要实现NSCopying 协议。
之前写的博客中有实现此协议的方法:OC读书笔记——对象复制

strong和copy的区别

strong关键字的作用就是使引用计数加一并持有该对象,使用改关键字的对象在生命周期结束后才会被释放。
一般我们都使用copy来修饰NSString,那么strong可以修饰NSString,答案当然是可以的,那么他俩有什么区别呢。
这里举例了一段代码:

import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) NSString* firstString;
@property (nonatomic, copy)   NSString* secondString;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSMutableString* textString = [[NSMutableString alloc] initWithString:@"str"];
    self.firstString = textString;
    self.secondString = textString;
    NSLog(@"textSting:%@,firstString:%@,secondStirng:%@",textString,_firstString,_secondString);
    NSLog(@"textSting:%p,firstString:%p,secondStirng:%p",textString,_firstString,_secondString);
}
[textString appendString:@"str"];
    NSLog(@"textSting:%@,firstString:%@,secondStirng:%@",textString,_firstString,_secondString);
    NSLog(@"textSting:%p,firstString:%p,secondStirng:%p",textString,_firstString,_secondString);

@end

这是打印结果:
在这里插入图片描述

可以看出在修改赋值的字符串后,使用strong修饰符的字符串内容改变了,而使用copy修饰符的字符串内容并无改变,这就是为什么一般使用copy而不是strong的原因,使用strong修饰,只是传递了指针,也就是浅拷贝,对原来数据进行修改,传递后的数据也会修改。
下面又举了一个例子:

#import "ViewController.h"

@interface ViewController ()
// strong  类型 NSArray
@property (nonatomic, strong) NSArray  *strongArray;
// copy    类型 NSArray
@property (nonatomic, copy)   NSArray  *copyedArray;
@property (nonatomic, strong) NSString* firstString;
@property (nonatomic, copy)   NSString* secondString;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSArray *tmpArray = [NSArray arrayWithObjects:@"1",@"2", nil];

    self.strongArray = tmpArray;
    self.copyedArray = tmpArray;

    NSLog(@"tmpArray: %@, %p", tmpArray, tmpArray);
    NSLog(@"self.strongArray: %@, %p", self.strongArray, self.strongArray);
    NSLog(@"self.copyedArray: %@, %p", self.copyedArray, self.copyedArray);

打印结果如下:在这里插入图片描述
我们发现他们三个的地址居然是相同的,仔细观察,第一个例子的来源对象属于可变类型,第二个例子的来源对象是不可变类型。
可以得出如下结论:
如果数据来源是可变对象,那么使用strong和copy的地址是不同的,copy会进行深拷贝出一个新的地址的副本。
而当数据来源是不可变对象,那么使用strong和copy的地址是想同的,都指向来源数组的内存地址。
如果确定来源是不可变类型,这种情况下用strong类型。因为copy修饰的不可变类型在进行set操作时,底层进行了这样的判断if ([str isMemberOfClass: [不可变NS class]]),如果来源是可变的,就进行一次深拷贝,如果是不可变的就和strong修饰一样,进行一次浅拷贝,
当项目庞大时,有成百上千个NSArray对象,多少会损耗app性能。
注意:如果来源对象是一个可变数组,尽管使用copy关键字,但是copy后数组内的指针还是指向原来数组元素指针指向的对象,如果该对象改变,那么copy的数组元素也会改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值