iOS 开发中常见Property关键字解读

来源:互联网 发布:河边软件职业技术学院 编辑:程序博客网 时间:2024/06/07 22:46

(一) weak assgin retain strong unsafe_unretained copy


背景

ARC苹果新引用了strongweak对象变量属性。

ARC引入了新的对象的新生命周期限定,即零弱引用。如果零弱引用指向的对象被deallocated的话,零弱引用的对象会被自动设置为nil。

strong 与 weak 强应用与弱引用

强引用也就是我们通常所讲的引用,其存亡直接决定了所指对象的存亡。如果不存在指向一个对象的引用,并且此对象不再显示列表中,则此对象会被从内存中释放。

弱引用除了不决定对象的存亡外,其它与强引用相同。即使一个对象被持有无数个若引用,只要没有强引用指向他,那么其还是会被清除。

使用强引用指向的对象,引用计数器会+1

使用弱引用指向的对象,引用计数器不变化

strong 与 retain

在ARC下,用strong代替了retain,strong等同于retain。

声明Block,使用retain,Block会在++栈++中。必须使用copy关键字,才能使Block在堆中。

声明Block, 使用strong,Block会在++堆++中。

weak 与assign

assign: 简单赋值,不更改索引计数

weak比assign多了一个功能,当对象消失后自动把指针变成nil

一个简单例子

@interface ViewController ()@property (nonatomic, weak)   NSDate *weakDate;@property (nonatomic, assign) NSDate *assignDate;@property (nonatomic, strong) NSDate *strongDate;@end
_strongDate = [NSDate date];_weakDate = _strongDate;_assignDate = _strongDate;NSLog(@"_strongDate %p, %p, %@", _strongDate, &_strongDate, _strongDate);NSLog(@"_weakDate %p, %p, %@", _weakDate, &_weakDate, _weakDate);NSLog(@"_assignDate %p, %p, %@", _assignDate, &_assignDate, _assignDate);_strongDate = nil;NSLog(@"\n");   NSLog(@"_strongDate : %@", _strongDate);NSLog(@"_weakDate : %@", _weakDate);NSLog(@"_assignDate : %@", _assignDate);
_strongDate 0x60400000c160, 0x7f997ec38608, Wed Nov 15 10:38:24 2015_weakDate 0x60400000c160, 0x7f997ec385f0, Wed Nov 15 10:38:24 2015_assignDate 0x60400000c160, 0x7f997ec385f8, Wed Nov 15 10:38:24 2015_strongDate : (null)_weakDate : (null)(lldb) 

小结:

  • weak 修饰对象时候,当对象被释放掉后,指针会指向 nil
  • strong 修饰对象时候,当对象被释掉后,指针会指向 nil
  • assgin 修饰对象时候,当对象被释掉后,会产生悬空指针,再次调用会导致程序崩溃。
  • assgin 一般用在修饰基础数据类型

后期新增 ++unsafe_unretained++(等价assgin)

__unsafe_unretained 主要跟 C 代码交互。另外 __weak 是有代价的,需要检查对象是否已经消亡,而为了知道是否已经消亡,自然也需要一些信息去跟踪对象的使用情况。__unsafe_unretained 比 __weak 快。当明确知道对象的生命期时,选择 __unsafe_unretained 会有一些性能提升。当 A 拥有 B 对象,当 A 消亡时 B 也消亡。这样当 B 存在,A 就一定会存在。而 B 又要调用 A 的接口时,B 就可以存储 A 的 __unsafe_unretained 指针。

在细微不同的方式下,__unsafe_unretained和__weak都防止了参数的持有。对于__weak,指针的对象在它指向的对象释放的时候回转换为nil,这是一种特别安全的行为。就像他的名字表达那样,__unsafe_unretained会继续指向对象存在的那个内存,即使是在它已经销毁之后。这会导致因为访问那个已释放对象引起的崩溃。

__weak只支持iOS 5.0和OS X Mountain Lion作为部署版本。如果你想部署回iOS 4.0和OS X Snow Leopark,你就不得不用__unsafe_unretained标识符。(了解即可)

(二) 原子性和非原子性 atomic和 nonatomic


//@property(nonatomic, retain) UITextField *userName;//系统生成的代码如下:- (UITextField *) userName {        return userName;}- (void) setUserName:(UITextField *)userName_ {       [userName_ retain];       [userName release];       userName = userName_;}

而 atomic 版本的要复杂一些

//@property(retain) UITextField *userName;//系统生成的代码如下:- (UITextField *) userName {     UITextField *retval = nil;     @synchronized(self) {       retval = [[userName retain] autorelease];     }    return retval;}- (void) setUserName:(UITextField *)userName_ {     @synchronized(self) {          [userName release];          userName = [userName_ retain];     }}

简单来说,就是 atomic 会加一个锁来保障线程安全,并且引用计数会 +1,来向调用者保证这个对象会一直存在。假如不这样做,如有另一个线程调 setter,可能会出现线程竞态,导致引用计数降到0,原来那个对象就释放掉了。要注意那个锁++并不能++『保证线程安全』

(三) 深复制与浅复制


概念

对象拷贝有两种方式:浅复制和深复制。

浅复制,并不拷贝对象本身,仅仅是拷贝指向对象的指针。

深复制, 是直接拷贝整个对象内存到另一块内存中。

再简单些说:浅复制就是指针拷贝;深复制就是内容拷贝

copy与mutableCopy方法
  • copy返回immutable(不可变)对象;
  • mutableCopy返回mutable对象;
1、 NSString非集合类对象的copy与mutableCopy

场景1: 原对象为immutable不可变对象

NSString *origin = @"origin";NSString *stringCopy = [string copy];NSMutableString *stringMCopy = [string mutableCopy];

可以通过查看内存,可以看到 stringCopy 和 origin 的地址一样的,进行了指针的拷贝。而 stringMCopy 的地址和 origin 不一样,进行了内容拷贝;

场景2: 原对象为mutable可变对象

NSMutableString *origin = [NSMutableString stringWithString: @"origin"];NSString *stringCopy = [origin copy];NSMutableString *mStringCopy = [origin copy];NSMutableString *stringMCopy = [origin mutableCopy];origin         : 0x60000024f630, 0x7fff5d559788, originstringCopy     : 0xa006e696769726f6, 0x7fff5d559780, origin_strongString  : 0xa006e696769726f6, 0x7fff5d559778, originstringMCopy    : 0x600000248580, 0x7fff5d559770, origin

看内存,发现 string、stringCopy、mStringCopy、stringMCopy 四个对象的内存地址都不一样,说明此时都是做内容拷贝。

综上两个例子,我们可以得出结论:

在非集合类对象中:对immutable对象进行copy操作,是指针复制,mutableCopy操作时内容复制;对mutable对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
- [immutableObject copy] // 浅复制
- [immutableObject mutableCopy] //深复制
- [mutableObject copy] //深复制
- [mutableObject mutableCopy] //深复制

2、 NSArray集合类对象的copy与mutableCopy

集合类对象是指NSArray、NSDictionary、NSSet … 之类的对象。:

场景1: 原对象为immutable不可变对象

NSArray *array = @[@[@"a", @"b"], @[@"c", @"d"]];NSArray *copyArray = [array copy];NSMutableArray *mCopyArray = [array mutableCopy];array      : 0x60000003ef00, 0x7fff558cb750copyArray  : 0x60000003ef00, 0x7fff558cb748mCopyArray : 0x6000002596b0, 0x7fff558cb740
  1. 查看内容,可以看到copyArray和array的地址是一样的,而mCopyArray和array的地址是不同的。说明copy操作进行了指针拷贝,mutableCopy进行了内容拷贝。

  2. 需要强调的是:此处的内容拷贝,仅仅是拷贝array这个对象,array集合内部的元素仍然是指针拷贝。(可以自行去验证)

场景2: 原对象为immutable不可变对象

NSMutableArray *array = [NSMutableArray arrayWithObjects:@"",@"b",@"c",nil];NSArray *copyArray = [array copy];NSMutableArray *mCopyArray = [array mutableCopy];array      : 0x60000025ce00, 0x7fff57481788copyArray  : 0x600000441aa0, 0x7fff57481780mCopyArray : 0x60000025d8b0, 0x7fff57481778

copyArray、mCopyArray和array的内存地址都不一样,说明copyArray、mCopyArray都对array进行了内容拷贝。

综上两个例子,我们可以得出结论:

在集合类对象中,对immutable对象进行copy,是指针复制,mutableCopy是内容复制;对mutable对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对象元素仍然是指针复制。用代码简单表示如下:
[immutableObject copy] // 浅复制
[immutableObject mutableCopy] //单层深复制
[mutableObject copy] //单层深复制
[mutableObject mutableCopy] //单层深复制

单层深复制: 容器对象进行深拷贝,容器内部对象浅拷贝。

p str 会打印对象本身的内存地址和对象内容po &str 则打印的是引用对象的指针所在的地址
阅读全文
0 0