原型

原型模式解決的問題

對象的創(chuàng)建特別復雜,當兩個對象在抽象邏輯完全一致,只在實例化的細節(jié)略有差異,如果要重新創(chuàng)建一個對象,不如拷貝自己再去修改。
原型模式,在多數(shù)情況下可被理解為一種深拷貝的行為。在 OC 中使用原型模式,首先要遵循 NSCoping 協(xié)議。
原型模式在實際開發(fā)中的應用場景:
1.對象不能直接通過初始化函數(shù)創(chuàng)建出來,且創(chuàng)建過程不具有普遍性還比較復雜。
2.不同的實例間的差距很小(僅僅幾個屬性值不同),因此復制相應數(shù)量的原型比重新實例化創(chuàng)建更方便,代碼更少。
3.類的依賴過多,創(chuàng)建條件比較嚴格的時候也可以考慮使用。

NSCopying

NScopying是一個與對象拷貝有關的協(xié)議。如果想讓一個類的對象支持拷貝,就需要讓該類實現(xiàn)NSCopying協(xié)議。NSCopying協(xié)議中的聲明的方法只有一個- (id)copyWithZone:(NSZone *)zone。如果想讓一個對象支持拷貝,就要實現(xiàn)這個方法。(NSMutableCopying 與 NSCopying類似)。

#import <Foundation/Foundation.h>

@interface Student : NSObject<NSCopying>
@property (nonatomic, copy)NSString* name;
@property (nonatomic, copy)NSString* sex;
@property (nonatomic, copy)NSString *height;
- (id)initWithDictionary:(NSDictionary *)dictionary;
@end
#import "Student.h"

@implementation Student
- (id)initWithDictionary:(NSDictionary *)dictionary
{
    self = [super init];
    if (self) {
        self.name = dictionary[@"name"];
        self.sex = dictionary[@"sex"];
        self.height = dictionary[@"height"];
    }
    return self;
}
- (id)copyWithZone:(nullable NSZone *)zone
{
    Student *student = [[Student alloc] init];
    student.name = self.name;
    student.sex = self.sex;
    student.height = self.height;
    return student;
}
@end

下面我們針對Student這個類做一些測試。

    @property (nonatomic, copy) Student *copStudent;
    NSDictionary *dictionary = @{@"name": @"楊千嬅染了紅頭發(fā)", @"sex": @"男", @"height": @"182cm"};
    Student *student1 = [[Student alloc] initWithDictionary:dictionary];
    NSLog(@"student1 name: %@, sex: %@, height: %@", student1.name, student1.sex, student1.height);
    NSLog(@"student1的地址: %p", student1);

    Student *student2 = student1;
    NSLog(@"student2 name: %@, sex: %@, height: %@", student2.name, student2.sex, student2.height);
    NSLog(@"student2的地址: %p", student2);

    Student *student3 = student1.copy;
    NSLog(@"student3 name: %@, sex: %@, height: %@", student3.name, student3.sex, student3.height);
    NSLog(@"student3的地址: %p", student3);

    self.copStudent = student1;
    NSLog(@"self.copStudent name: %@, sex: %@, height: %@", self.copStudent.name, self.copStudent.sex, self.copStudent.height);
    NSLog(@"self.copStudent的地址: %p", self.copStudent);

打印結果

 student1 name: 楊千嬅染了紅頭發(fā), sex: 男, height: 182cm
 student1的地址: 0x608000030000
 student2 name: 楊千嬅染了紅頭發(fā), sex: 男, height: 182cm
 student2的地址: 0x608000030000
 student3 name: 楊千嬅染了紅頭發(fā), sex: 男, height: 182cm
 student3的地址: 0x60800002ff80
 self.copStudent name: 楊千嬅染了紅頭發(fā), sex: 男, height: 182cm
 self.copStudent的地址: 0x608000030240

打印結果顯示:
student2(student2 = student1)的地址與student1地址是相同的。[0x608000030000]
student3(student3 = student1.copy)[0x60800002ff80]self.copStudent(self.copStudent = student1)[0x608000030240]的地址是與student1[0x608000030000]的地址不同的。

探究下原因:

Student *student2 = student1;

這個=代表的一個賦值的操作,在Objective-C有兩類數(shù)據(jù)類型,一種是基本數(shù)據(jù)類型,另一種是NSObject對象。
對于基本數(shù)據(jù)類型,賦值操作會創(chuàng)建一個源對象的副本,一個新的對象。

對于NSObject對象,賦值操作相當于復制了指針而非對象,賦值操作使得源指針和新指針都指向了同一個NSObject對象。
這就是student1student2地址一樣的原因,更嚴格的說不是student1student2的地址一樣,是student1student2都指向了同一個地址。在Object-C中,我們創(chuàng)建對象通常用這樣的方式NSObject *objc = [[NSObject alloc] init];習慣性將objc稱為NSObject的實例化對象,但是實際上objc是一個指針變量,它存放著[[NSObject alloc] init]這個對象的地址,或者說objc指向這個對象,當我們要訪問所創(chuàng)建的對象時,先要讀取指針變量objc存儲的目標對象的值,再通通過該地址訪問目標對象的內(nèi)存單元,這是一個間接訪問。

    NSLog(@"student1: %x", (int)&student1);
    NSLog(@"student2: %x", (int)&student2);

打印結果:

student1: 591c97e0
student2: 591c97d8

這說明student1、student2兩個指針自身的地址是不一樣的,但是它們都指向了[[Student alloc] initWithDictionary:dictionary];存放的地址。

 Student *student3 = student1.copy;
 self.copStudent = student1;

student3self.copStudent的地址雖然都和student1的地址不一樣,但是兩者還是有區(qū)別的。

Student *student3 = student1.copy;這行代碼的執(zhí)行順序是這樣的:
1.student1.copy先執(zhí)行Student.h文件里面我們已經(jīng)實現(xiàn)的NSCopying協(xié)議(- (id)copyWithZone:(nullable NSZone *)zone),生成一個新的Student實例對象。
2.將上一步生成的新的實例對象賦值給student3。

@property (nonatomic, copy) Student *copStudent;
self.copStudent = student1;

@property = ivar + getter + setter

ivar 是實例變量,編譯器會幫我們自動生成名字為_屬性名這樣的實例變量,同時也會自動生成settergetter方法。
copy@property的語義設置關鍵字,不同的關鍵字,屬性的setter、getter的內(nèi)部實現(xiàn)不一樣。
self.copStudent = student1;
這里就是調(diào)用copStudentsetter方法(語義關鍵字是copy),這就是兩者的區(qū)別。
最后要提一點:
copy可以分為淺拷貝、深拷貝、完全拷貝。

淺拷貝(shallow copy): 在淺拷貝操作時,對于被拷貝的對象的每一層都是指針拷貝。
深拷貝(one-level-deep copy): 在深拷貝操作時,對于被拷貝的對象,至少一層是深拷貝。
完全拷貝(real-deep copy): 在完全拷貝操作時,對于被拷貝對象的每一層都是深拷貝。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 原型模式 原型模式是非常簡單的一種模式,在我們的實際開發(fā)中經(jīng)常用到這種模式,例如你創(chuàng)建的可變字典、可變字符串調(diào)用c...
    小帥798閱讀 545評論 0 0
  • 轉至元數(shù)據(jù)結尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,068評論 0 9
  • 基本概念 原型模式:用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象 從上圖可以看到,Prototy...
    傻傻小蘿卜閱讀 737評論 0 1
  • 原型模式 介紹 在許多面向對象的應用程序中,有些對象的創(chuàng)建代價過大或者過于復雜。要是可以重建相同的對象并作輕微的改...
    666真666閱讀 527評論 0 2
  • 我是紫君 我的原創(chuàng) 都說嫁出去的女兒,潑出去的水。那么,嫁出去的女兒要不要繼承父母的財產(chǎn)呢? 很多開明的家庭,特...
    昭潔紫君閱讀 4,548評論 1 3

友情鏈接更多精彩內(nèi)容