首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C Copy 機制深度解析:淺拷貝 vs 深拷貝、copy vs mutableCopy

     編輯:關於C語言

核心概念

  • 淺拷貝:指針拷貝,不創建新對象,僅增加引用計數(retain)
  • 深拷貝:對象拷貝,創建新對象副本,引用計數為 1

Foundation 集合類別行為規律

NSString / NSMutableString

源對象調用方法結果類型拷貝類型說明
NSStringcopyNSString淺不可變→不可變,返回自身,retain+1
NSStringmutableCopyNSMutableString深不可變→可變,必須創建新對象
NSMutableStringcopyNSString深可變→不可變,創建不可變副本
NSMutableStringmutableCopyNSMutableString深可變→可變,創建可變副本

結論:只有「不可變對象 → copy → 不可變對象」是淺拷貝,其餘均為深拷貝。

代碼驗證

// 淺拷貝示例
NSString *str = @"abcd";
NSString *str2 = [str copy];
NSLog(@"str: %p, str2: %p"); // 地址相同

// 深拷貝示例
NSMutableString *mStr = [NSMutableString stringWithString:@"abcd"];
NSString *mStr2 = [mStr copy];
NSLog(@"mStr: %p, mStr2: %p"); // 地址不同

NSArray / NSMutableArray 同理

規律完全一致:不可變→copy→不可變 = 淺拷貝;其他組合 = 深拷貝。

自定義對象實現 NSCopying

協議要求

@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end

@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end

標準實現模板

@interface Person : NSObject 
@property (nonatomic, assign) int age;
@property (nonatomic, copy) NSString *name;
- (instancetype)initWithAge:(int)age withName:(NSString *)name;
@end

@implementation Person
- (instancetype)initWithAge:(int)age withName:(NSString *)name {
    if (self = [super init]) {
        _age = age;
        _name = name; // copy 屬性自動複製字符串
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone {
    Person *copy = [[[self class] allocWithZone:zone] 
                    initWithAge:_age withName:_name];
    return copy;
}
@end

測試

Person *p1 = [[Person alloc] initWithAge:20 withName:@"Jack"];
Person *p2 = [p1 copy];
NSLog(@"p1: %p, p2: %p"); // 地址不同 → 深拷貝

不可變自定義對象的 copy

若對象邏輯上不可變,可直接返回自身:

- (id)copyWithZone:(NSZone *)zone {
    return self; // 淺拷貝,等同 NSString 行為
}

繼承鏈中的 copy 實現

子類必須重寫

子類繼承父類實現的 copyWithZone: 但必須重寫以拷貝自身新增屬性:

@interface Student : Person
@property (nonatomic, copy) NSString *school;
- (instancetype)initWithAge:(int)age withName:(NSString *)name withSchool:(NSString *)school;
@end

@implementation Student
- (instancetype)initWithAge:(int)age withName:(NSString *)name withSchool:(NSString *)school {
    if (self = [super initWithAge:age withName:name]) {
        _school = school;
    }
    return self;
}

- (id)copyWithZone:(NSZone *)zone {
    Student *copy = [super copyWithZone:zone]; // 先調用父類拷貝
    copy.school = _school;                     // 再拷貝自身屬性
    return copy;
}
@end

最佳實踐總結

  1. 屬性聲明用 copy:@property (copy) NSString *name; 確保 setter 執行拷貝
  2. 集合屬性用 copy:防止外部修改內部集合
  3. 實現 NSCopying:自定義模型類必須實現,支持集合拷貝、歸檔等
  4. 區分 copy/mutableCopy:根據語義選擇,不可變接口返回不可變對象
  5. 子類重寫必調 super:[super copyWithZone:zone] 保證父類屬性正確拷貝

常見陷阱

  • 以為 copy 一定是深拷貝 → 導致共享可變狀態 Bug
  • 忘記實現 NSCopying 卻調用 copy → 運行時崩潰
  • 子類不重寫 copyWithZone: → 新增屬性未拷貝
  • 在 copyWithZone: 中直接賦值指針而非拷貝 → 淺拷貝風險
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved