一、assign屬性
當數據類型爲int、float等原生類型時,可以使用assign,否則可能導致內存泄露。例如當使用malloc分配了一塊內存,並把它的地址賦值給了指針a,後來如果希望指針b也共享這塊內存,於是講a賦值給(assgin)b。這時就用到了assgin,此時a和b指向同一塊內存。但是現在問題出現了,當a不再需要這塊內存時,能都直接釋放呢?肯定是不能的,因爲a並不知道b是否還在使用這塊內存,如果a釋放了,那麼b在使用這塊內存的時候引起程序crash掉。
二、retain屬性
retain屬性就是爲了解決上述問題而提出的,使用了引用計數(reference counting),還是上面那個例子,我們給那塊內存設一個引用計數,當內存唄分配並且賦值給a時,引用計數是1.當把a賦值給b時引用計數增加到2.這時如果a不再使用這塊內存,它只需要把引用計數減1,表明自己不再擁有這塊內存。b不再使用這塊內存時也把引用計數減1.當引用計數變爲0的時候,代表該內存不再被任何指針所引用,系統可以直接釋放掉。此時系統自動調用dealloc函數,內存被回收。
三、copy屬性
copy是創建一個新對象,兩個對象內容相同,舊對象沒有變化。新的對象retain爲1,與舊有對象的引用計數不變。舊對象發生改變不影響新對象,copy減少對象對上下文的依賴。
在iOS開發中我們一般都這麼定義:@property (nonatomic,copy) NSString *name,而不這麼定義:@property (nonatomic,retain) NSString *name,兩者的差別就在一個使用copy,一個使用retain。 這是爲什麼呢? 在說明白retain和copy的區別,首先需要明白深複製和淺複製的概念。
1 深複製:內容拷貝,源對象和副本對象指的是兩個不同的對象,源對象引用計數器不變,副本對象引用計數器爲1
2 淺複製:指針拷貝,源對象和副本對象指的都是同一個對象,對象引用計數器+1,相當於retain
只有不可變對象創建不可變副本(copy)纔是淺複製,其它的都是深複製
下面通過實驗來說明copy和retain的區別。
@interface ViewController ()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,retain) NSString *name2;
@end
-(void)test{
NSString *str = @"我是不可變的";
self.name = str;
self.name2 = str;
NSLog(@" str: %p",str);
NSLog(@" copy: %p",self.name);
NSLog(@"retain: %p",self.name2);
}
-(void)test2{
NSMutableString *str = [NSMutableString stringWithString:@"我是可變的"];
self.name = str;
self.name2 = str;
NSLog(@" strM: %p",str);
NSLog(@" copy: %p",self.name);
NSLog(@"retain: %p",self.name2);
}
兩次執行的結果如下:
str: 0x7b140
copy: 0x7b140
retain: 0x7b140
str: 0x9c142b80
copy: 0x9c140e60
retain: 0x9c142b80
首先,在test中,str指向一個不可變的NSString對象,地址爲0x7b140,然後str分別給name和name2賦值,由於name和name2都是NSString對象,所有都屬於淺複製,賦值後都是指向str對象地址0x7b140,所有打印結果三者指向同一個對象。 其次,在test2中,str指向一個可變的NSMutableString對象,地址爲0x9c140e60,然後分別給name和name2賦值,此時copy對應的name是深複製,所以會複製出另一個對象,地址爲0x7c148b80。而retain對應的name2依然指向str對象地址0x9c142b80,所以打印結果是str和name2對應同一地址,name對應另一個地址。
所以得出結論:
(1)copy是創建一個新對象,兩個對象內容相同,舊對象沒有變化。新的對象retain爲1,與舊有對象的引用計數不變。舊對象發生改變不影響新對象,copy減少對象對上下文的依賴。
(2)retain屬性表示兩個對象地址相同(建立一個指針,指針拷貝),內容相同,這個對象的retain值+1。兩個對象要改變就一起改變。
(3)如果把一個對象賦值給另一個對象(如上面把str賦值給name或name2),如果該對象是不可變的,那麼另一個對象是copy或者retain都可以,
沒區別;把一個對象賦值給另一個對象,如果該對象是可變的,並且希望另一個對象隨着該對象變化而變化,則可以把另一個對象設置爲retain(如上面把str賦值給name2);如果希望另一個對象不隨着該對象變化而變化,則可以把另一個對象設置爲copy(如上面把str賦值給name)。