天天看點

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

7.1 字元串(NSString與NSMutableString)

NSString:字元序列不可變的字元串

NSMutableString:字元序列可變的字元串

7.1.1 建立字元串

NSString功能:

  • 建立字元串: 建立字元串既可以使用以init開頭的執行個體方法,也可以使用以string開 頭的類方法,當然也可以直接使用@“”的形式給出字元串直接量
  • 讀取檔案或網絡URL來初始化字元串
  • 将字元串内容寫入檔案或URL
  • 擷取字元串長度,既可以擷取字元串内包括的字元個數,也可以擷取字元串包括的位元組個數
  • 擷取字元串中的字元或位元組,既可擷取指定位置的字元,也可以擷取指定範圍的字元
  • 擷取字元串對應的C風格字元串
  • 連接配接字元串
  • 分隔字元串
  • 查找字元串内指定的字元和子串替換字元串
  • 比較字元串
  • 字元串大小比較
  • 對字元串中的字元進行大小寫轉換

NSString的常見用法:

unichar data[6] = {97, 98, 99, 100, 101, 102};
        //使用Unicode數值數組初始化字元串
        NSString* str = [[NSString alloc] initWithCharacters:data length:6];
        NSLog(@"%@", str);
        char* cstr = "hello,ios";
        //将C風格的字元串轉換為NSString對象
        NSString* str2 = [NSString stringWithUTF8String:cstr];
        NSLog(@"%@", str2);
        //将字元串寫入指定檔案
        [str2 writeToFile:@"myFile.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil];
        //讀取檔案内容,用檔案内容初始化字元串
        NSString* str3 = [NSString stringWithContentsOfFile:@"myFile.txt" encoding:NSUTF8StringEncoding error:nil];
        NSLog(@"%@", str3);
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

7.1.2 NSString的常用功能

NSString的常見用法:

NSString* str = @"hello";
        NSString* book = @"《瘋狂iOS講義》";
        //在str後面追加強定的字元串
        //原來的字元串對象并不改變,隻是将新生成的字元串重新賦給str指針變量
        str = [str stringByAppendingString:@",iOS!"];
        NSLog(@"%@", str);
        //擷取字元串對應的C風格字元串
        const char* cstr = [str UTF8String];
        NSLog(@"擷取的C字元串:%s", cstr);
        //在str後面追加帶變量的字元串
        //原來的字元串對象并不改變,隻是将新生成的字元串重新賦給str指針變量
        str = [str stringByAppendingFormat:@"%@是一本非常不錯的圖書。", book];
        NSLog(@"%@", str);
        NSLog(@"str的字元個數為:%lu", [str length]);
        NSLog(@"str按UTF-8字元集解碼後位元組數為:%lu", [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
        //擷取str的前10個字元組成的字元串
        NSString* s1 = [str substringToIndex:10];
        NSLog(@"%@", s1);
        //擷取str第5個字元之後的字元串
        NSString* s2 = [str substringFromIndex:5];
        NSLog(@"%@", s2);
        //擷取str從第5個字元之後,數15個字元組成的字元串
        NSString* s3 = [str substringWithRange:NSMakeRange(5, 15)];
        NSLog(@"%@", s3);
        //擷取iOS在str中出現的位置
        NSRange pos = [str rangeOfString:@"iOS"];
        NSLog(@"iOS在str中出現的開始位置:%ld,長度為:%ld", pos.location, pos.length);
        //将str的所有字元大寫
        str = [str uppercaseString];
        NSLog(@"%@", str);
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

NSRange:

NSRange并不是一個類,它隻是一個結構體,它包括了location和length兩個unsigned int整型值,分别代表起始位置和長度。Objecetive-C還提供了NSMakeRange()函數來建立NSRange變量。

7.1.3 可變字元串(NSMutableString)

NSString類是不可變的類,即一旦NSSting對象被建立,包含在這個對象中的字元序列是不可改變的,直至這個對象被銷毀。

NSMutableString對象則代表一個字元串序列可變的字元串,而且NSMutableString是NSString的子類,是以NSString的方法,NSMutableString都可以直接使用,NSMutableString對象也可以直接當成NSString對象使用。

NSMutableString提供:appendFormat:、appendString:、deleteCharactersInRange:、insertString:atIndex:、replaceCharactersInRange:withString:、replaceOccurrencesOfString:options:range:、setString:方即可改變該字元串所包含的字元序列。

NSMutableString常見用法:

NSString* book =@"《瘋狂iOS講義》";
        //建立一個NSMutableString對象
        NSMutableString* str = [NSMutableString stringWithString:@"Hello"];
        //追加強定的字元串
        //字元串所包含的字元序列本身發生了改變,是以無須重新指派
        [str appendString:@",iOS!"];
        NSLog(@"%@", str);
        //追加帶變量的字元串
        //字元串所包含的字元序列本身發生了改變,是以無須重新指派
        [str appendFormat:@"%@是一本非常不錯的圖書。", book];
        NSLog(@"%@", str);
        //在指定位置插入字元串
        //字元串所包含的字元序列本身發生了改變,是以無須重新指派
        [str insertString:@"fkit.org" atIndex:6];
        NSLog(@"%@", str);
        //删除從位置6開始之後的12個字元
        [str deleteCharactersInRange:NSMakeRange(6, 12)];
        NSLog(@"%@", str);
        //将從位置6開始數9個字元将這9個字元替換成Objective-C
        [str replaceCharactersInRange:NSMakeRange(6, 9) withString:@"Objective-C"];
        NSLog(@"%@", str);
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

7.2 日期與時間

Objective-C為處理日期、時間提供了NSDate、NSCalendar對象,還提供了日期格式器來處理日期與字元串之間的轉換。

7.2.1 日期與時間(NSDate)

NSDate對象代表日期與時間,Objective-C中既提供了類方法來建立NSDate對象,也提供了大量以init開頭的方法來初始化NSDate對象。

NSDate常見方法的用途:

//擷取代表目前日期、時間的NSDate
        NSDate* date1 = [NSDate date];
        NSLog(@"%@", date1);
        //擷取從目前時間開始,一天之後的日期
        NSDate* date2 = [[NSDate alloc]
                         initWithTimeIntervalSinceNow:3600 * 24];
        NSLog(@"%@", date2);
        //擷取從目前時間開始,3天之前的日期
        NSDate* date3 = [[NSDate alloc]
                         initWithTimeIntervalSinceNow:-3 * 3600 * 24];
        NSLog(@"%@", date3);
        //擷取從1970年1月1日開始,20年之後的日期
        NSDate* date4 = [NSDate dateWithTimeIntervalSince1970:3600 * 24 * 366 * 20];
        NSLog(@"%@", date4);
        //擷取系統目前的Locale區域設定
        NSLocale* cn  = [NSLocale currentLocale];
        //擷取NSDate在目前Locale下對應的字元串
        NSLog(@"%@", [date1 descriptionWithLocale:cn]);
        //擷取兩個日期之間較早的日期
        NSDate* earlier = [date1 earlierDate:date2];
        NSLog(@"較早的日期為:%@", earlier);
        //擷取兩個日期之間較晚的日期
        NSDate* later = [date1 laterDate:date2];
        NSLog(@"較晚的日期為:%@", later);
        //比較兩個日期,compare:方法傳回NSComparisonResult枚舉值,
        //該枚舉類型包含NSOrderedAscending、NSOrderedSame和
        //NSOrderedDescending三個值
        //分别代表調用compare:的日期位于被比較日期之前、相同、之後
        switch ([date1 compare:date3]) {
            case NSOrderedAscending:
                NSLog(@"date1位于date3之前");
                break;
            case NSOrderedSame:
                NSLog(@"date1與date3日期相等");
                break;
            case NSOrderedDescending:
                NSLog(@"date1位于date3之後");
                break;
        }
        //擷取兩個時間之間的時間差
        //date1 - date3
        NSLog(@"date1與date3之間時間差%g秒"
              , [date1 timeIntervalSinceDate:date3]);
        //擷取指定時間與現在的時間差
        //date2 - now
        NSLog(@"date2與現在時間差%g秒"
              , [date2 timeIntervalSinceNow]);
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

建立NSDate的類方法與執行個體方法基本相似,但NSDate的類方法以date開頭,而執行個體方法以init開頭。兩個NSDate之間可以比較大小,可以計算時間差,也可以把NSDate轉化為複合目前NSLocale的格式字元串。

NSLocale:

NSLocale代表一個語言、國際環境,比如大陸的簡體中文,就可以通過NSLocale對象來代表。同樣一個日期,在不同的語言、國家環境下,顯示出來的字元串是不同的。

7.2.2 日期格式器(NSDateFormatter)

NSDateFormatter代表一個日期格式器,它的功能就是完成NSDate與NSString之間的轉換。

使用NSDateFormatter完成NSDate與NSString之間的轉換的步驟如下:

1.建立一個NSDateFormatter對象。

2.調用NSDateFormatter的setDateStyle:、setTimeStyle:方法設定格式化日期、時間的風格。 其中,日期、時間風格支援如下幾個枚舉值。

NSDateFormatterNoStyle:不顯示日期、時間的風格。

NSDateFormatterShortStyle:顯示“短”的日期,時間風格

NSSDateFormatterMediumStyle:顯示“中等”的日期、時間風格。

NSSDateFormatterLongStyle:顯示“長”的日期、時間風格。

NSSDateFormatterFullStyle:顯示“完整”的日期、時間風格。

如果打算使用自己的格式模版,調用NSDateFormatter的setDateFormate:方法設定日期、時間模版即可。

3.如果需要将NSDate轉化為NSString,調用NSDateFormatter的stringFromDate:方法執行格式化即可;如果需要将NSString轉化為NSDate,調用NSDateFormatter的dateFromString:方法執行格式化即可。

簡單示範一下NSDateFormatter的功能和用法:

//需要被格式化的時間
        //擷取從1970年1月1日開始,20年之後的日期
        NSDate* dt = [NSDate dateWithTimeIntervalSince1970:
                      3600 * 24 * 366 * 20];
        //建立兩個NSLocale,分别代表中國、美國
        NSLocale* locales[] = {
            [[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]
            ,[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"]
        };
        NSDateFormatter* df[8];
        //為上面兩個NSLocale建立8個DateFormat對象
        for (int i = 0; i < 2; i++) {
            df[i * 4] = [[NSDateFormatter alloc] init];
            //設定NSDateFormatter的日期、時間風格
            [df[i * 4] setDateStyle:NSDateFormatterShortStyle];
            [df[i * 4] setTimeStyle:NSDateFormatterShortStyle];
            //設定NSDateFormatter的NSLocale
            [df[i * 4] setLocale:locales[i]];
            df[i * 4 + 1] = [[NSDateFormatter alloc] init];
            //設定NSDateFormatter的日期、時間風格
            [df[i * 4 + 1] setDateStyle:NSDateFormatterMediumStyle];
            [df[i * 4 + 1] setTimeStyle:NSDateFormatterMediumStyle];
            //設定NSDateFormatter的NSLocale
            [df[i * 4 + 1] setLocale:locales[i]];
            df[i * 4 + 2] = [[NSDateFormatter alloc] init];
            //設定NSDateFormatter的日期、時間風格
            [df[i * 4 + 2] setDateStyle:NSDateFormatterLongStyle];
            [df[i * 4 + 2] setTimeStyle:NSDateFormatterLongStyle];
            //設定NSDateFormatter的NSLocale
            [df[i * 4 + 2] setLocale:locales[i]];
            df[i * 4 + 3] = [[NSDateFormatter alloc] init];
            //設定NSDateFormatter的日期、時間風格
            [df[i * 4 + 3] setDateStyle:NSDateFormatterFullStyle];
            [df[i * 4 + 3] setTimeStyle:NSDateFormatterFullStyle];
            //設定NSDateFormatter的NSLocale
            [df[i * 4 + 3] setLocale:locales[i]];
        }
        for (int i = 0; i < 2; i++) {
            switch (i) {
                case 0:
                    NSLog(@"-------中國日期格式-------");
                    break;
                    
                case 1:
                    NSLog(@"-------美國日期格式-------");
                    break;
            }
            NSLog(@"SHORT格式的日期格式:%@", [df[i * 4] stringFromDate:dt]);
            NSLog(@"MEDIUM格式的日期格式:%@", [df[i * 4 + 1] stringFromDate:dt]);
            NSLog(@"LONG格式的日期格式:%@", [df[i * 4 + 2] stringFromDate:dt]);
            NSLog(@"FULL格式的日期格式:%@", [df[i * 4 + 3] stringFromDate:dt]);
        }
        NSDateFormatter* df2 = [[NSDateFormatter alloc] init];
        //設定自定義的格式器模版
        [df2 setDateFormat:@"公元yyyy年MM月dd日 HH時mm分"];
        //執行格式化
        NSLog(@"%@", [df2 stringFromDate:dt]);
        NSString* dateStr = @"2013-03-02";
        NSDateFormatter* df3 = [[NSDateFormatter alloc] init];
        //根據日期字元串的格式設定格式模版
        [df3 setDateFormat:@"yyyy-MM-dd"];
        //将字元串轉換為NSDate對象
        NSDate* date2 = [df3 dateFromString:dateStr];
        NSLog(@"%@", date2);
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

其中,日期格式化字元串(dateFormat)涉及的格式占位符有

G  // AD,即公元
 
yy  // 顯示年份的末尾兩位
yyyy  // 顯示完整年份
 
M  // 1-9月顯示一位數字(1 ~ 9),10-12月顯示兩位數字(10 ~ 12)
MM  // 顯示兩位數組月份(01 ~ 12)
MMM  // 英文月份縮寫
MMMM  // 英文月份全拼
 
d // 1-9日顯示一位數字(1~9),10-31日顯示兩位數字(10~31)
dd  // 日期顯示兩位數字(01~31)
 
EEE  // 星期幾縮寫(例如星期二,英文是Tue,中文是周二)
EEEE  // 星期幾全拼(例如星期二,英文是Tuesday,中文是星期二)
 
aa  // 加上這個,會顯示AM/PM
 
H  // 24小時值,0-9小時顯示一位數字(0~9),10-12小時顯示兩位數字(10~12)
HH  // 24小時制,顯示兩位數字
K  //  12小時制,0-9小時顯示一位數字(0~9),10-12小時顯示兩位數字(10~12)
KK  //  12小時制,顯示兩位數字
 
m  // 0-9分顯示一位數字(0~9),10-59分顯示兩位數字(10~59)
mm  // 顯示兩位數字(00~59)
 
s  // 0-9分顯示一位數字(0~9),10-59分顯示兩位數字(10~59)
ss  // 分鐘,顯示兩位數字(00~59)
S  // 顯示毫秒


           

7.2.3 月曆(NSCalendar)與日期元件(NSDateComponents)

為了能分開處理NSDate對象所包含的各個字段的資料,Foundation架構提供了NSCalendar對象,該對象包含如下兩個常用方法:

  • (NSDateComponents*)components:fromDate: :從NSDate提取年、月、日、時、分、秒各時間段的資訊。
  • dateFromComponents:(NSDateComponents*)comps :使用comps對象包含的年、月、時、分、秒各時間字段的資訊來建立NSDate。

NSDateComponents

該對象專門用于封裝年、月、日、時、分、秒各時間字段的資訊。它隻包含了對year、month、date、day、hour、minute、second、week、weekday等各字段的setter和getter方法。

從NSDate對象中分開擷取各時間字段的數值的步驟如下:

  • 1.建立NSCalendar對象。
  • 2.調用NSCalendar的components:fromDate:方法擷取NSDate對象中各時間字段的數值,該方法傳回一個NSDateComponents對象。
  • 3.調用NSDateComponents對象的getter方法來擷取各時間字段的數值。

使用各時間字段的數值來初始化NSDate對象的步驟如下:

  • 1.建立NSCalendar對象。
  • 2.建立一個NSDateComponents對象,調用該對象的setter方法來設定各時間字段的值。
  • 3.調用NSCalendar的dateFromComponents:(NSDateComponents*)初始化NSDate對象,該方法會傳回一個NSDate對象。
//擷取代表公曆的Calendar對象
        NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        //擷取目前日期
        NSDate* dt = [NSDate date];
        unsigned unitFlags = NSYearCalendarUnit|NSMonthCalendarUnit|
                            NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|
                            NSSecondCalendarUnit|NSWeekdayCalendarUnit;
        //擷取不同時間字段的資訊
        NSDateComponents* comp = [gregorian components:unitFlags fromDate:dt];
        //擷取各個時間字段的數值
        NSLog(@"現在是%ld年", comp.year);
        NSLog(@"現在是%ld月", comp.month);
        NSLog(@"現在是%ld日", comp.day);
        NSLog(@"現在是%ld時", comp.hour);
        NSLog(@"現在是%ld分", comp.minute);
        NSLog(@"現在是%ld秒", comp.second);
        NSLog(@"現在是星期%ld", comp.weekday);
        //再次建立一個NSDateComponents對象
        NSDateComponents* comp2 = [[NSDateComponents alloc] init];
        //設定各個時間字段的數值
        comp2.year = 2013;
        comp2.month = 4;
        comp2.day = 12;
        comp2.hour = 15;
        comp2.minute = 34;
        //通過NSDateComponents所包含的時間字段的數值來恢複NSDateduixiang
        NSDate* date = [gregorian dateFromComponents:comp2];   
        NSLog(@"擷取的日期為:%@", date);
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

7.2.4 定時器(NSTimer)

使用定時器程式設計操作步驟:

  • 1.調用NSTimer的scheduledTimerWithTimeInterval:invocation:或scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:類方法來建立NSTimer對象。調用該類方法時,需要傳入如下參數。

timeInterval:指定每隔多少秒執行一次任務。

invocation或target與selector:指定重複執行的任務。如果指定target和selector參數,則指定用某個對象的特定方法作為重複執行的任務;如果指定invocation參數,該參數需要傳入一個NSInvocation對象,其中NSInvocation對象也是封裝target和selector的,其實也是指定用某個對象的特定方法作為重複執行任務。

userInfo:該參數用于傳入額外的附加資訊。

repeats:該參數需要指定一個BOOL值,該參數控制是否需要重複執行任務。

  • 2.為第一步編寫方法。
  • 3.銷毀定時器。調用定時器的invalidate方法即可。

測試定時器:

@interface NSTimerTest : NSObject
@property (nonatomic, assign) int count;
- (void) start;
- (void) stop;
@end

@implementation NSTimerTest {
    NSTimer* timer;
}
@synthesize count;
- (void) start {
    timer = [NSTimer scheduledTimerWithTimeInterval:0.5
                                     target:self  //指定以目前對象的info方法作為執行體
                                   selector:@selector(start)
                                   userInfo:nil
                                    repeats:YES];  //指定重複執行
    NSLog(@"正在執行第%d次任務", self.count++);
}

- (void) stop {
    NSLog(@"取消執行定時器");
    [timer invalidate];
}
@end

int main(int grac, char *grav[]) {
    @autoreleasepool {
        NSTimerTest* test = [[NSTimerTest alloc] init];
        for (int i = 0; i < 10; i++) {
            [test start];
        }
        [test stop];
    }
    return 0;
}
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

7.3 對象複制

NSObject類提供了copy和multableCopy方法,通過這兩個方法即可複制已有對象的副本。

7.3.1 copy與mutableCopy方法

  • copy方法用于複制對象的副本。通常來說,copy方法總是傳回對象的不可修改的副本,即使該對象本身是可修改的。
調用NSMutableString的copy方法,将會傳回不可修改的字元串對象。
  • mutableCopy方法用于複制對象的可變副本。通常來說,mutableCopy方法總是傳回該對象可修改的副本,即使被複制的對象本身是不可修改的,調用mutableCopy方法複制出來的副本也是可修改的。
程式調用NSString的mutableCopy方法,将會傳回一個NSMutableString對象。

對複制對象的副本無論怎麼修改,原對象都不會發生改變。

NSMutableString* book = [NSMutableString
                                 stringWithString:@"瘋狂iOS講義"];
        //複制book字元串的可變副本
        NSMutableString* bookCopy = [book mutableCopy];
        //修改副本,對原字元串沒有任何影響
        [bookCopy replaceCharactersInRange:NSMakeRange(2, 3)
                                withString:@"Android"];
        //此處看到原字元串的值并沒有改變
        NSLog(@"book的值為:%@", book);
        //字元串副本發生了改變
        NSLog(@"bookCopy的值為:%@", bookCopy);
        NSString* str = @"fkit";
        //複制str(不可變字元串)的可變副本
        NSMutableString* strCopy = [str mutableCopy];
        //向可變字元串後面追加字元串
        [strCopy appendString:@".org"];
        NSLog(@"%@", strCopy);
        //調用book(可變字元串)的copy方法,程式傳回一個不可修改的副本
        NSMutableString* bookCopy2 = [book copy];
        //由于bookCopy2是不可修改的,是以下面的代碼将會出現錯誤
        //[bookCopy2 appendString:@"aa"];
           

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

7.3.2 NSCopying與NSMutableCopy協定

雖然NSObject提供了copy和mutableCopy方法,但自定義類并不能直接調用這兩個方法來複制自身。

為了保證一個對象可以調用copy方法來複制自身的不可變副本,通常需要做如下事情:

  • 讓該類實作NSCopying協定。
  • 讓該類實作copyWithZone:方法。

為了保證一個對象可以調用mutableCopy方法來複制自身的可變副本,通常需要做如下事情:

  • 讓該類實作NSMutableCopying協定。
  • 讓該類實作mutableCopyWithZone:方法。

下面實作類的copy:

@interface FKDog : NSObject <NSCopying>
@property (nonatomic, strong) NSMutableString* name;
@property (nonatomic, assign) int age;
@end

@implementation FKDog
@synthesize name;
@synthesize age;
- (id) copyWithZone:(NSZone*) zone {
    NSLog(@"--執行copyWithZone--");
    FKDog* dog = [[[self class] allocWithZone:zone] init];
    dog.name = self.name;
    dog.age = self.age;
    return dog;
}
@end

int main(int grac, char *grav[]) {
    @autoreleasepool {
        FKDog* dog1 = [[FKDog alloc] init];
        dog1.name = [NSMutableString stringWithString:@"旺财"];
        dog1.age = 20;
        FKDog* dog2 = [dog1 copy];
        dog2.name = [NSMutableString stringWithString:@"snoopy"];
        dog2.age = 12;
        NSLog(@"dog1的名字為:%@", dog1.name);
        NSLog(@"dog1的年齡為:%d", dog1.age);
        NSLog(@"dog2的名字為:%@", dog2.name);
        NSLog(@"dog2的年齡為:%d", dog2.age);
        
    }
    return 0;
}
           

協定系統自帶!

輸出結果為:

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制
  • 當程式調用上述類的copy方法來複制自身時,底層實際上調用了copyWithZone:方法來執行實際的複制操作。因為此處的類沒有提供對應的不可變類,自然也就無法複制不可變的該類的對象。如果程式為該類提供了不可變類,當然還是應該讓該類的copyWithZone:傳回不可變的該類的對象。
  • 如果重寫copyWithZone:方法時,其父類已經實作NSCopying協定,并重寫過copyWithZone:方法,那麼子類重寫copyWithZone:方法應先調用父類的copy方法複制從父類繼承得到的成員變量,然後對子類定義的成員變量進行指派。

假如父類已經重寫copyWithZone:方法,那麼子類重寫copyWithZone:方法的格式如下:

- (void) copyWithZone: (NSZone*) zone {
   id obj = [super copy];
   //對子類定義的成員變量進行指派。
   ...
   return obj;
}
           

7.3.3 淺複制與深複制

淺複制(shallow copy)

當對象的執行個體變量時指針變量時,如果程式隻是複制該指針的位址,而不是真正複制指針所指向的對象,這種方式就被稱為“淺複制”。

淺複制出來的兩個對象是兩個指針變量,他們指向的是同一個位址。

【iOS】—— Foundation架構(一)7.1 字元串(NSString與NSMutableString)7.2 日期與時間7.3 對象複制

深複制(deep copy)

深複制不僅會複制對象本身,而且會“遞歸”複制每個指針類型的執行個體變量,直到兩個對象沒有任何公共的部分。

将上述的copyWithZone:改為深複制:

- (id) copyWithZone: (NSZone*) zone {
    NSLog(@"--執行copyWithZone--");
    FKDog* dog = [[[self class] allocWithZone:zone] init];
    dog.name = [self.name mutableCopy];
    dog.age = self.age;
    return dog;
}
           

上面程式中并沒有簡單地将被複制對象的name執行個體變量的值賦給新對象的name執行個體變量,而是先将原對象name執行個體變量複制了一份可變副本,再将該可變副本的值賦給新對象的name執行個體變量。這樣就保證了原對象和新對象沒有任何公共部分,這就實作了深複制。

7.3.4 setter方法的複制選項

前面介紹合成setter和getter方法時提到可以使用copy訓示符,copy訓示符就是指定當程式調用setter方法複制時,實際上是将傳入參數的副本賦給程式的執行個體變量。

定義合成getter、setter方法時并沒有提供mutableCopy訓示符,隻有copy訓示符。

系統預設的setter方法如下:

例如setName:

- (void) setName: (NSMutableString*) aname {
   name = [aname copy];
}
           

copy方法預設是複制該對象的不可變副本,雖然程式傳入的NSMutableString,但程式調用該參數的copy方法得到的是不可變副本。