# How to Implement NSCopying and NSCoding in MulleObjC

> Learn to implement NSCopying and NSCoding in MulleObjC. Define copy, encodeWithCoder, and initWithCoder to manage object copying and serialization efficiently.

- Repository: [mulle-objc/mulleobjc](https://github.com/mulle-objc/mulleobjc)
- Tags: how-to-guide
- Published: 2026-03-07

---

**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/main/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:

```objc
- (id)copy;

```

For **immutable objects**, implement this method to return a retained reference to self:

```objc
- (id)copy
{
    return [self retain];
}

```

For **mutable objects**, allocate a new instance and duplicate the internal state:

```objc
- (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:

```objc
- (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:

```objc
- (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/main/src/protocol/NSCopying.h)](https://github.com/mulle-objc/mulleobjc/blob/master/src/protocol/NSCopying.h), this protocol requires:

```objc
- (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/main/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`](https://github.com/mulle-objc/mulleobjc/blob/master/src/class/NSObject+NSCodingSupport.h).

### Required Archiving Methods

Every class adopting NSCoding must implement:

```objc
- (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:

```objc
- (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:

```objc
- (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:

```objc
- (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:

```objc
- (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:

```objc
// 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/main/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/main/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`](https://github.com/mulle-objc/mulleobjc/blob/master/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/main/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)copy` in 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 `retain` decoded objects in `initWithCoder:`.
- **Subclassing** mandates calling `[super copy]` before copying subclass-specific ivars.
- **Versioning and class clusters** utilize the `NSObject+NSCodingSupport.h` category 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`](https://github.com/mulle-objc/mulleobjc/blob/main/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.