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 則打印的是引用對象的指針所在的地址
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章