How does NSCopying work, and why use a copy property for strings and blocks?
Predict first
What does this print?
let a = NSArray(array: [1])
print((a.copy() as AnyObject) === a)
- true
- false
Predict first
What does this print?
let s = NSMutableString(string: "a")
let snapshot = s.copy() as! NSString
s.append("b")
print(snapshot, s)
- ab ab
- a ab
- a a
Mental model. Copying a laminated card just hands you the same card. Copying a whiteboard photographs what it says now, so later scribbles do not change your picture.
NSCopying gives copy, NSMutableCopying gives mutableCopy. For an immutable object a conforming class may return itself, retained, from copy: copying NSArray or NSString is free. A mutable object’s copy returns a new immutable snapshot, and its mutableCopy a new mutable one. Both are shallow for collections: the elements are shared, not duplicated.
That is why Objective-C properties holding NSString, NSArray, NSDictionary or blocks use the copy attribute: someone may pass an NSMutableString, and storing it with strong lets them mutate your state behind your back. copy takes a snapshot (or returns the same immutable instance for free). Blocks need it for a different reason: a block starts on the stack, and copy moves it to the heap so it survives. In Swift the equivalent is the @NSCopying attribute, and value types make most of this unnecessary.
An immutable object has nothing to protect, so copy just returns the same instance, retained.
copy on a mutable string returns an immutable snapshot of its current contents. Later changes affect only the original.
Common traps
- Declaring a
strongNSStringproperty. A mutable string passed in can change under you. - Expecting
copyon an array to duplicate its elements. It is shallow: each element is still the same object.
They might follow up with
- How do you implement
NSCopying? Return a new instance with the same state incopy(with:), or returnselffor an immutable class. - What does a deep copy need? Copying each element too, usually with archiving or a hand-written method.
Related questions
- 2.9.4 Which Foundation types are value types in Swift, which are classes, and what happens to mutable ObjC types?
- 4.7.2 How do Objective-C blocks differ from Swift closures, especially for capture?
← What is a class cluster, and what does it change for subclassing and…