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

(一) 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, origin
stringCopy     : 0xa006e696769726f6, 0x7fff5d559780, origin
_strongString  : 0xa006e696769726f6, 0x7fff5d559778, origin
stringMCopy    : 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, 0x7fff558cb750
copyArray  : 0x60000003ef00, 0x7fff558cb748
mCopyArray : 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, 0x7fff57481788
copyArray  : 0x600000441aa0, 0x7fff57481780
mCopyArray : 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 则打印的是引用对象的指针所在的地址
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章