Copy程序員
主要內容:less
1. copy的基本使用優化
♠ copy的效果:atom
對源對象進行copy,創建出新的副本,彼此修改互不干擾!spa
♠ OC中有兩種copy方式設計
1> copy指針
若是對象有可變/不可變之分,copy只能copy出不可變版本,若是沒有此區分,copy方法就是創建一個副本。code
2> mutableCopy對象
創建對象的可變副本(僅僅是當對象有可變和不可變版本時才須要是要本方法)blog
@implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self copyDemo1]; NSLog(@"------------------"); [self copyDemo2]; } - (void)copyDemo1{ NSString *str1 = @"copy1"; NSLog(@"%@ %p",[str1 class],str1); // copy => 不可變 不產生新對象 至關於引用計數器+1 id obj = [str1 copy]; NSLog(@"%@ %p",[obj class],obj); // mutableCopy => 可變 產生新對象 id obj2 = [str1 mutableCopy]; NSLog(@"%@ %p",[obj2 class],obj2); } - (void)copyDemo2{ NSMutableString *str2 = [NSMutableString stringWithString:@"copy2"]; NSLog(@"%@ %p",[str2 class],str2); // copy => 不可變 產生新對象 id obj1 = [str2 copy]; NSLog(@"%@ %p",[obj1 class],obj1); // mutableCopy => 可變,產生新對象 id obj2 = [str2 mutableCopy]; NSLog(@"%@ %p",[obj2 class],obj2); } @end
♠ 小結:
可變 ---> 不可變
可變 ---> 可變
不可變 ---> 可變 以上3種都會產生新的對象(深拷貝)
不可變 ---> 不可變 不會產生新對象,僅僅是對計數器+1(淺拷貝)
2. 自定義對象的copy屬性
♠ 當一個對象的NSString類型屬性使用strong 修飾
@property (nonatomic,strong) NSString *name;
若是對該屬性作以下設置
person *p = [[person alloc]init]; NSMutableString *str = [NSMutableString stringWithString:@"aaaa"]; p.name = str; NSLog(@"%@",[p.name class]); // 輸出結果:__NSCFString [str setString:@"bbbb"]; NSLog(@"%@",[p.name class]); // 輸出結果:__NSCFString
從打印輸出能夠發現:
p.name屬性的類型已經變爲NSMutableString類型,而與咱們當初定義的類型不一致,爲何?
由於一個對象的準確類型是在給改對象「分配內存空間」的時候制定的類型,而程序員指定屬性爲某對象的類型以後就能夠具備該對象的方法,而可否運行成功取決於該屬性的實際類型,若是使用了實際類型不存在的方法,將會報"unrecognized selector send to instance"
例如:
id aa = [[NSObject alloc]init]; NSString *string = aa; NSLog(@"%zd",string.length);
此代碼塊中,定義了NSString類型的變量,試圖指向了NSObject的對象,當調用NSString的length方法時,編譯能夠經過,可是因爲string的實際類型是NSObject類型,並不存在該方法,全部會報方法不存在錯誤:
unrecognized selector sent to instance 0x7f9628c8b7f0'
發現問題:
當咱們使用strong修飾成員屬性時,將會有多個指針指向相同的對象,並可對其進行操做,而在程序設計過程當中,有些時候咱們的操做並不但願影響源數據,此時可能就須要改變變量的修飾爲copy
♠ 面向對象程序開發中,有一個很是重要的原則
開閉原則:
- 開:對內開放,想怎麼改,就怎麼改
- 閉:對外封閉,只能用,不能改
定義成copy的屬性,在設置時會默認進行一次copy操做;
-> 對可變屬性進行copy ———— 新建副本
-> 對不可變屬性進行copy ———— 不會建立新的對象,只是計數器+1,跟strong類型一致的。
使用注意:
對於有可變與不可變之分類型的屬性而言,因爲copy操做獲得的將是不可變類型,全部對於可變類型的屬性,不該該使用copy去修飾。
3. 支持copy的自定義對象
先來看程序,有自定義類,有兩個成員屬性
@property (nonatomic,copy)NSString *name; @property (nonatomic,assign) int age;
控制器中實例化對象,並嘗試使用copy
person *p1 = [[person alloc] init]; p1.name = @"aaa"; p1.age = 12; person *p2 = [p1 copy]; p2.name = @"bbb"; p2.age = 14;
運行可發現:
-[person copyWithZone:] 系統將提示沒有實現copyWithZone方法;
那麼問題來了,copyWithZone:與copy方法有什麼關係?
蘋果官方文檔中描述:
Returns the object returned by copyWithZone:, This is a convenience method for classes that adopt the NSCopyingprotocol. An exception is raised if there is no implementation for copyWithZone:. NSObject does not itself support the NSCopyingprotocol. Subclasses must support the protocol and implement the copyWithZone: method. A subclass version of the copyWithZone: method should send the message to super first, to incorporate its implementation, unless the subclass descends directly from NSObject.
咱們能夠從中獲得一些信息:
1> copy 是 copyWithZone的簡化形式,若是沒有實現copyWithZone方法而調用copy會出現異常;
2> NSObject 類自己並無遵照NSCopying協議,子類想要使用copy方法,必須遵照協議,而且實現copyWithZone方法;
3> 子類實現copyWithZone:方法必須先調用父類的copyWithZone,除非子類直接繼承於NSObject
也就是說若要是的咱們自定義類可以拷貝,有兩個條件:
* 類遵照NSCopying協議
* 類實現copyWithZone:方法
Zone: 分配對象是須要內存空間的,若是指定了zone,就能夠指定新對象的內存空間,可是zone是一個很是古老的技術,爲避免在堆中出現碎片而使用的,現在已幾乎不用。
- (id)copyWithZone:(NSZone *)zone{ person *p = [[self.class alloc]init]; p.name = self.name; p.age = self.age; return p; }
self.class 的緣由:
1> copyWithZone: 是一個對象方法,self.class 得到類對象
2> 保證建立的對象都是person類或者子類對象
若是父類也實現了copyWithZone:方法,必須調用父類copyWithZone:方法。
4. copy修飾的結構體
問題:block類型的變量爲何要用copy修飾?
由於在ARC下,編譯器底層對block作了一些優化,能夠防止出現內存泄露,若是使用了strong,至關於強引用了一個棧區變量,從內存管理的角度而言,程序員須要管理的僅僅是堆區,全部在對block類型變量進行復制時要使用copy,對值進行一次copy操做,將其copy到堆區。