Repetition, repetition, repetition. I don’t like it. I like code to be as dry as it can possibly be without affecting readability, so as soon as I find myself repeating code in a project I immediately refactor. And if I find myself repeatedly writing what many would consider boilerplate code, I begin to wonder how I can avoid doing this in the future.
I was recently adding a couple of properties to an existing class using a category. We can do this thanks to the Objective-C runtime functions
objc_setAssociatedObject. I found myself writing the same old getters and setters that looked identical to those I’d written countless times before. And herein lies the problem. I don’t like repeating myself.
Below is an example of an Objective-C category with some very important properties. It’s a surprise these don’t exist on the underlying class.
1 2 3 4
The nicest solution I came up with was to simply declare the properties as dynamic and implement the necessary accessor methods at runtime, from the
1 2 3 4 5 6 7 8 9 10 11 12 13 14
I think it looks nice. And if you have multiple properties in your category then it can save a lot of lines of code, and ultimately it avoids repetition. The documentation tells us that a category
+load method is called after the class’s own
+load method. The important thing here is that implementing the
+load method within a category does not override the
+load method of the class itself.
I’ve found that the
+implementDynamicPropertyAccessors method is always sufficient for my needs. But the other methods may provide peace of mind as well as the ability to use this functionality on a class that has other dynamic properties that you don’t wish to be implemented in this way.
All the magic happens in the
+[NSObject implementDynamicPropertyAccessors] method defined in a category on NSObject.
The method itself is very simple.
1 2 3 4 5 6
All I’m doing here is iterating over the properties of
self, and sending each property off to another method, which will do all the hard work.
+implementAccessorsIfNecessaryForProperty: will inspect the attributes of the property to determine whether or not the accessors need to be implemented. It will then implement the accessor methods if necessary.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
- First I fetch the attributes of the property and check if the property is dynamic. If the property is not dynamic then we have no business with it.
- Next I go through the attributes to determine the type of the property and the custom getter and setter names, if specified. If the property is not an object type, then we have no business with it (I don’t currently support properties that are not of an object type).
- Next I create a key to be used as a reference to the associated object. Note: this is a static variable, which I increase for each property being implemented, allowing for 232 properties to be implemented on each object (or 264 for a 64-bit binary), which I think is more than sufficient.
- And finally, I get the name of the property and implement the getter and setter methods. The setter is only implemented if the property is not marked as readonly. In reality the property will very rarely be readonly, because something has to set the value. I occasionally define a readonly property in a category and set it lazily when it’s read. I might do this, for example, with an NSMutableArray property, but I’m not supporting that kind of functionality here.
The Missing Pieces
Among the missing pieces are the methods that actually implement the getter and setter.
1 2 3 4 5 6 7
The method above defines the getter using the property name or a custom getter name if specified. It then calls another method to create the implementation based on the block specified. The block returns the associated object using the key as the reference.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
The method above defines the setter using the property name – capitalized and prefixed with
set – or a custom setter name if specified. It also has to inspect the attributes to determine the association policy, which must be provided when setting an associated object. The block used here simply sets the associated object using the specified key as the reference.
1 2 3 4 5 6 7 8
The method above adds a method to the class for the given selector and implementation block.
Finally, I defined a couple of other methods to separate out concerns. The following method iterates over the properties and executes the given block with each property
1 2 3 4 5 6 7 8 9 10 11
And the following method simply returns an array of the attributes of the given property.
1 2 3 4
Thanks for reading! If you got this far, I hope it’s because you found it interesting. If I’m right, then you might like to check out the code, which is available on GitHub. It’s part of a project to demo how it can be used, and is tweaked to include extra methods