Design Patterns in iOS — Prototype
Introduction I used to lead a small iOS team, spending my days dealing with endless inter-team friction while also serving as the lead developer writing code. It was high-pressure, low-reward work. Recently I moved to a new environment (not a top-tier team, but still) and finally have the mental space to dig into the details. In fact, the team's high standards for code quality and architectural design push me to approach my work from a higher and deeper perspective. The design patterns rabbit hole is going to take at least ten to twenty posts to cover. Prototype Pattern Prototype Pattern: Specifies the kind of objects to create using a prototype instance, and creates new objects by copying that prototype. In plain terms: in Objective-C, you can use deep copying to quickly and conveniently create new objects. Deep Copy vs. Shallow Copy in iOS Class Diagram declares the interface for cloning itself. , as a concrete implementation of , implements the operation. The client here refers to a class that uses an instance of the prototype. The client creates a new object — a copy of the prototype — via . When to Use It 1. The object to be created should be independent of its type and creation process. That is, the object cannot be created simply by calling an initializer; the creation process is complex or non-generic. 2. The class to be instantiated is determined at runtime. At the time of writing, you don't know which object will be created or what its internal structure looks like (e.g., the complexity depends on user actions). 3. You want to avoid a factory hierarchy that mirrors the product hierarchy. Rather than controlling object creation through factory methods or abstract factories, you want to clone objects directly. 4. Differences between instances of different classes are only a matter of state combinations. Copying an appropriate number of prototypes is more convenient than manually instantiating them. 5. A class is hard to create — for example, a composite object where each component can have other components as children. Cloning an existing composite object and modifying the copy is easier. The internal structure is complex and difficult to reproduce from scratch. Two common use cases: 1. You have many related classes that behave similarly but differ mainly in internal properties such as names or images. 2. You need a composite (tree-structured) object as the basis for something else — for example, using a composite object as a component to build another composite object. In short: when creating an object via initializers is cumbersome or even impossible, consider using the Prototype pattern to deep-copy a model as a starting point. How to Use It Example code: - inherits from (which implements ). When a instance receives a message, forwards the message to subclasses that implement the protocol. So must implement , otherwise it will error. - is just shorthand for using the default zone. It's rare to call directly; defining and implementing it is required to conform to . So you would normally only see inside the implementation of . Similarly, you would typically avoid overriding , and let the default implementation call through to . What is the difference between "-copy" and "-copyWithZone:"? — In short: implement , call . - Using allows subclasses to use this method as well. However, be careful when a subclass has additional properties. uses its parent class 's implementation, but since was never assigned in that method, the copied instance has as . Should override and manually reassign all properties? That's not elegant. Here's a more framework-level approach: Pay attention to properties of complex types such as or : The property was properly copied, but unfortunately the elements inside and were not. Nobody wants to share their girlfriend with someone else. The correct implementation: That's more like it. Now consider: has another property that stores two nested arrays — a two-dimensional array. Using still doesn't achieve a "true" copy of the nested elements. In that case, use: Note that since the array elements are instances, needs to implement the protocol methods. Alternatively, third-party frameworks like or can handle this quickly. I've gone a bit far down the rabbit hole. Prototype Pattern in Cocoa Beyond the various methods discussed above, there's actually another classic example of the Prototype pattern in Cocoa: Zombie Objects (). Although what's being copied here is a class object rather than an instance, since classes in Objective-C are also special objects, understanding this through the lens of the Prototype pattern is reasonable. See Using Zombie Objects to Aid Debugging for details. References Deep Copy vs. Shallow Copy in iOS Code All code in this article can be found in my GitHub repository .