2016年2月24日 星期三

iOS tips - weak, strong, copy, , retain, assign概念介紹

寫了obj-c也兩年多了,但對於obj-c的properity一直沒有弄得很清楚,網路上找到了一篇文章之後,對於一些觀念有了比較清楚的認知,以下主要是參考這篇文章的心得
http://rypress.com/tutorials/objective-c/properties


在一個class中宣告的string, array, dictionary或者是其他object,在obj-c中可以使用property來定義此object被其他class使用getter, setter存取時的屬性,而obj-c有一些關鍵字可以讓我們來使用及定義,如weak, strong, copy, retain, assign,下面會一一來介紹

首先瞭解obj-c的memory management control方式


從這張圖可以清楚的了解,obj-c的回收機制就是系統會判斷一個物件的owner有幾個,當一個物件沒有人來own時,就會回收此物件
使用ARC的話,我們可以不用去擔心哪時要回收此物件,因為compiler會幫我們做好判斷,但我們仍然是需要瞭解porpery的屬性,這樣才能設定正確的存取關係,避免造成retain cycles

strong : 基本上這是最常用的屬性,以下圖的sample code來說明,首先我們宣告了一個person物件,接著我們又宣告了一個car物件,並在car物件中宣告了使用person物件並使用strongㄕ
// Person.h
#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic) NSString *name;

@end
// Person.m
#import "Person.h"

@implementation Person

- (NSString *)description {
    return self.name;
}

@end
// Car.h
#import <Foundation/Foundation.h>
#import "Person.h"

@interface Car : NSObject

@property (nonatomic) NSString *model;
@property (nonatomic, strong) Person *driver;

@end
接著考慮下面這段code
// main.m
#import <Foundation/Foundation.h>
#import "Car.h"
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *john = [[Person alloc] init];
        john.name = @"John";
        
        Car *honda = [[Car alloc] init];
        honda.model = @"Honda Civic";
        honda.driver = john;
        
        NSLog(@"%@ is driving the %@", honda.driver, honda.model);
    }
    return 0;
}
如此一來我們能夠確保在car物件需要person物件時,person物件是會一直存在以供使用的


weak : 接下來考慮下面的sample code,假設在person物件中也宣告了一個car物件,而屬性也設定成strong
// Person.h
#import <Foundation/Foundation.h>

@class Car;

@interface Person : NSObject

@property (nonatomic) NSString *name;
@property (nonatomic, strong) Car *car;

@end
接著在main.m加上這段code
honda.driver = john;
john.car = honda;       // Add this line
如此一來,很明顯的就會造成retain cycles了,以圖示來表示的話就像下面這樣
這兩個物件彼此互相擁有,任何一個物件的ownership永遠不會為0,系統無法回收這兩個物件就算他們沒人使用了,然後就會造成memory leak,之後你的app就會crash,此時老闆就不開心了.要解決這個問題,就是不使用strong屬性而使用weak
@property (nonatomic, weak) Car *car;
以圖來表示的話像是下面這樣:
可以看到car物件的owner是0,所以當honda被回收時,john也可以跟著說再見了,而不會死賴著不走造成crash


copy : copy屬性,copy屬性有點類似strong,但不同的是不會增加一個owners,而會如其名的複製一個物件給他,而通常開發者會在NSString中使用copy屬性.
// Car.h
@property (nonatomic, copy) NSString *model;
上面的code中我們在car物件宣告了一個model string並使用了copy,接著來看下面的code
// main.m
#import <Foundation/Foundation.h>
#import "Car.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *honda = [[Car alloc] init];
        NSMutableString *model = [NSMutableString stringWithString:@"Honda Civic"];
        honda.model = model;
        
        NSLog(@"%@", honda.model);
        [model setString:@"Nissan Versa"];
        NSLog(@"%@", honda.model);            // Still "Honda Civic"
    }
    return 0;
}
我們就能夠明白,當我們對honda物件的model屬性設定好值之後,model string是被複製到car物件的,而不是共用ownership,從sample code來看,當我們再回去修改model的內容值為Nissan Versa時,honda的model value仍是Honda Civic而不是Nissan Versa,但copy不是人人能用,只有符合NSCopying Protocol的物件才能使用.

其他的屬性:
retain : 非ARC才會用到,但現在似乎沒人不使用ARC?!
assign : 基本類型的物件如float, int等等會使用到,這屬性並不會牽扯到任何memory management,就是給基本類型的物件來使用的

沒有留言:

張貼留言