Object duplication in Objective-C enables developers to generate independent replicas of data structures. This mechanism ensures that alterations made to a derived instance do not mutate the original source. The framework distinguishes between creating immutable and mutable duplicates through two primary instance methods.
Duplication Mechanisms
The copy method generates an immutable replica, suitable for standard string, array, and dictionary types. Conversely, mutableCopy yields a mutable variant, allowing subsequent modifications without constraints. Both operations require explicit protocol conformance:
NSCopyingmandates the implementation ofcopyWithZone:.NSMutableCopyingrequiresmutableCopyWithZone:.
@protocol NSCopying <NSObject>
- (id)copyWithZone:(nullable NSZone *)zone;
@end
@protocol NSMutableCopying <NSObject>
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
@end
Deep vs. Shallow Replication
The distinction between deep and shallow copying hinges on memory allocation and reference counting:
- Deep Copy: Allocates fresh memory for the duplicate. The source and replica reside at distinct addresses, functioning independently. The new instance starts with a reference count of one.
- Shallow Copy: Performs a pointer duplication. Both variables reference the identical memory block. The system increments the reference count rather than allocating new storage.
For immutable classes like NSString, invoking copy often results in a shallow operation for optimization, as the underlying data cannot change. Using mutableCopy on the same instance forces a deep copy to allow future mutations.
Property Attributes and Retention Policies
Declaring object properties with the copy attribute prevents external mutability from corrupting internal state. A common pattern involves storing string inputs as private copies:
@property (nonatomic, copy) NSString *userName;
Selecting the appropriate memory qualifier depends on the project's memory management model:
| Scenario | Qualifier | Use Case |
|---|---|---|
| ARC | copy |
NSString, Block objects |
| ARC | strong |
General Objective-C objects |
| ARC | weak |
Circular references, delegates |
| ARC | assign |
Primitives, C-structs, enums |
| Non-ARC | copy |
Strings, Blocks |
| Non-ARC | retain |
Standard objects |
| Non-ARC | assign |
Primitives, delegates |
Implementing Custom Duplication
To enable duplication for bespoke classes, conform to NSCopying and define the allocation logic within the required method. The historical NSZone parameter can safely be ignored in modern development.
@interface UserRecord : NSObject <NSCopying>
@property (nonatomic, copy) NSString *identifier;
@property (nonatomic, assign) NSInteger accessLevel;
@end
@implementation UserRecord
- (id)copyWithZone:(NSZone *)zone {
UserRecord *duplicate = [[[self class] allocWithZone:zone] init];
if (duplicate) {
duplicate.identifier = self.identifier;
duplicate.accessLevel = self.accessLevel;
}
return duplicate;
}
@end
When a caller executes [originalUser copy], the runtime delegates to copyWithZone:. The implementation instantiates a fresh UserRecord, mirrors the current properties, and returns the isolated instance. This guarantees that subsequent updates to identifier or accessLevel on the original object remain entirely decoupled from the duplicated record.