How to Implement NSCopying and NSCoding in MulleObjC
Implement NSCopying by defining - (id)copy to return a retained self for immutable objects or a new copied instance for mutable ones; implement NSCoding by adding -encodeWithCoder: and -initWithCoder: to serialize and deserialize your object's state.
MulleObjC follows the same architectural contract as Apple’s Objective-C for object copying and archiving, but implements these protocols in its own header files with streamlined method signatures and manual memory management. This guide demonstrates how to implement NSCopying and NSCoding in the mulle-objc/mulleobjc repository, including the specific file paths, runtime conventions, and protocol variations unique to this environment.
NSCopying Protocol Implementation
The NSCopying protocol in MulleObjC is defined in [src/protocol/NSCopying.h](https://github.com/mulle-objc/mulleobjc/blob/master/src/protocol/NSCopying.h). Unlike Apple’s Foundation, this implementation removes the legacy copyWithZone: requirement and standardizes on a single method signature.
Required Method and Basic Implementation
Your class must implement exactly one instance method:
- (id)copy;
For immutable objects, implement this method to return a retained reference to self:
- (id)copy
{
return [self retain];
}
For mutable objects, allocate a new instance and duplicate the internal state:
- (id)copy
{
MyClass *newObj = [[[self class] alloc] init];
newObj->_value = [_value copy]; // Deep copy of mutable members
// ... copy additional ivars ...
return newObj;
}
Legacy Compatibility with copyWithZone:
If you maintain legacy code that uses copyWithZone:, forward the modern -copy method to your existing implementation:
- (id)copy
{
return [self copyWithZone:NULL];
}
- (id)copyWithZone:(NSZone *)zone
{
MyClass *newObj = [[[self class] alloc] init];
newObj->_value = [_value copy];
return newObj;
}
Subclassing Considerations
When subclassing a class that already implements NSCopying, always invoke the superclass copy method before copying subclass-specific state:
- (id)copy
{
MySubclass *newObj = [super copy];
newObj->_subclassValue = [_subclassValue copy];
return newObj;
}
Immutable Copy Variants
MulleObjC provides the MulleObjCImmutableCopying protocol for classes that need to distinguish between mutable and immutable copies. Defined in [src/protocol/NSCopying.h](https://github.com/mulle-objc/mulleobjc/blob/master/src/protocol/NSCopying.h), this protocol requires:
- (id)immutableCopy;
Adopt this protocol alongside NSCopying (<NSCopying, MulleObjCImmutableCopying>) when your class hierarchy requires explicit immutable copy semantics.
NSCoding Protocol Implementation
The NSCoding protocol is defined in [src/protocol/NSCoding.h](https://github.com/mulle-objc/mulleobjc/blob/master/src/protocol/NSCoding.h). MulleObjC requires two essential methods for archiving support, supplemented by optional utilities in src/class/NSObject+NSCodingSupport.h.
Required Archiving Methods
Every class adopting NSCoding must implement:
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (instancetype)initWithCoder:(NSCoder *)aDecoder;
Encoding Object State
In encodeWithCoder:, persist each ivar that must survive archiving. Use type-specific encoding methods for primitives and objects:
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeInteger:_count forKey:@"count"];
[coder encodeObject:_name forKey:@"name"];
// For raw bytes:
// [coder encodeBytes:_buffer length:_length];
}
Decoding and Memory Management
MulleObjC uses manual retain/release memory management. When implementing -initWithCoder:, you must explicitly retain any objects returned by the decoder:
- (instancetype)initWithCoder:(NSCoder *)decoder
{
if ((self = [super init]))
{
_count = [decoder decodeIntegerForKey:@"count"];
_name = [[decoder decodeObjectForKey:@"name"] retain];
// For raw bytes:
// _buffer = [decoder decodeBytesWithReturnedLength:&_length];
}
return self;
}
Versioning and Class Cluster Support
The NSObject+NSCodingSupport.h category provides utilities for archive versioning and class substitution. Use +version and +setVersion: to track format changes across application releases.
For class clusters, override classForCoder to specify which concrete subclass should be instantiated during decoding:
- (Class)classForCoder
{
return [self class]; // Override for class-cluster families
}
Post-Decoding Hooks
MulleObjC supports an optional -decodeWithCoder: method for additional setup after initialization. Use this hook to replace placeholder objects or perform validation:
- (void)decodeWithCoder:(NSCoder *)decoder
{
// Custom post-decode logic
}
Complete Implementation Example
The following MyPoint class demonstrates full conformance to both protocols, including proper memory management for the MulleObjC runtime:
// MyPoint.h
#import "NSObject.h"
#import "NSCopying.h"
#import "NSCoding.h"
@interface MyPoint : NSObject <NSCopying, NSCoding>
{
NSInteger _x;
NSInteger _y;
}
- (instancetype)initWithX:(NSInteger)x y:(NSInteger)y;
- (NSInteger)x;
- (NSInteger)y;
@end
// MyPoint.m
#import "MyPoint.h"
@implementation MyPoint
- (instancetype)initWithX:(NSInteger)x y:(NSInteger)y
{
if ((self = [super init]))
{
_x = x;
_y = y;
}
return self;
}
#pragma mark - NSCopying
- (id)copy
{
// Immutable behavior: return retained self
return [self retain];
}
#pragma mark - NSCoding
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeInteger:_x forKey:@"x"];
[coder encodeInteger:_y forKey:@"y"];
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
if ((self = [super init]))
{
_x = [decoder decodeIntegerForKey:@"x"];
_y = [decoder decodeIntegerForKey:@"y"];
}
return self;
}
@end
Key Source Files in the Repository
| File | Purpose |
|---|---|
[src/protocol/NSCopying.h](https://github.com/mulle-objc/mulleobjc/blob/master/src/protocol/NSCopying.h) |
Defines the NSCopying protocol with the -copy method and MulleObjCImmutableCopying extension. |
[src/protocol/NSCoding.h](https://github.com/mulle-objc/mulleobjc/blob/master/src/protocol/NSCoding.h) |
Declares required archiving methods encodeWithCoder: and initWithCoder:. |
src/class/NSObject+NSCodingSupport.h |
Provides NSObject category with classForCoder, versioning utilities, and awakeAfterUsingCoder:. |
[src/protocol/NSCopyingWithAllocator.h](https://github.com/mulle-objc/mulleobjc/blob/master/src/protocol/NSCopyingWithAllocator.h) |
Legacy support for copying across different allocator schemes. |
Summary
- NSCopying requires only
- (id)copyin MulleObjC; return[self retain]for immutable objects or allocate new instances for mutable copies. - NSCoding requires
-encodeWithCoder:and-initWithCoder:to serialize and restore object state. - Memory management remains manual in MulleObjC; explicitly
retaindecoded objects ininitWithCoder:. - Subclassing mandates calling
[super copy]before copying subclass-specific ivars. - Versioning and class clusters utilize the
NSObject+NSCodingSupport.hcategory for archive compatibility and concrete class substitution.
Frequently Asked Questions
Does MulleObjC still use copyWithZone:?
No. According to the source in src/protocol/NSCopying.h, MulleObjC removes copyWithZone: from the NSCopying protocol and standardizes on - (id)copy. However, you can retain copyWithZone: in your implementation for backward compatibility by having -copy forward to copyWithZone:NULL.
How do I handle memory management when decoding objects in MulleObjC?
MulleObjC uses manual retain/release semantics. When implementing -initWithCoder:, you must explicitly retain any objects returned by the decoder, as shown in the MyPoint example: _name = [[decoder decodeObjectForKey:@"name"] retain]. Failure to retain will result in premature deallocation when the autorelease pool drains.
What is the difference between NSCopying and MulleObjCImmutableCopying?
NSCopying provides the standard -copy method. MulleObjCImmutableCopying, defined in the same header file, adds the -immutableCopy method for classes that need to explicitly distinguish between mutable and immutable copy operations, typically used in class hierarchies with both mutable and immutable variants.
Where are the NSCoding helper methods like classForCoder defined?
These utility methods are declared in src/class/NSObject+NSCodingSupport.h. This category on NSObject provides classForCoder, awakeAfterUsingCoder:, and versioning support (+version / +setVersion:) to assist with complex archiving scenarios and class cluster implementations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →