One of the most common question regarding swift extensions is about stored properties. How many times have you wished a stored property within your last-super-cool extension in order to parametizer some behaviours?
As you may know Swift does not allow stored properties into extensions. That’s by design: “Extensions may not contain stored properties.”
The most common newbie error is try to overcome the above by implementing something as follow:
Even the above code will compile with no errors, you will realize sooner that it is completely wrong; in fact, the cycling reference to the computed property will loop your code until the heap will be saturated by almost identical calls.
The second common approach is googling around. You will stumble across some nifty workarounds in order to add something that resemble the stored properties concept to use inside extensions (see References).
One of the most (ab)used workaround consist of relying into Objective-C runtime, by adopting a solution based on objc_getAssociatedObject and objc_setAssociatedObject functions.
Even if there is nothing wrong in these solutions it would be much more elegant a pure swift approach. That’s the reason about this post: attempting to create a pure swift extension with support for stored properties.
The proposed solution is very simple. It is based on the assumption that extensions don’t accept stored properties but computed ones. By elaborating and digging around the by-value Structs concept, we eventually hack the extensions design adding, in fact, the concept of stored properties. Completely in Swift.
A proof-of-concept is a lot more useful than thousands of words. So here we go:
The Holder struct will contain the private value that our computed property will expose to the world, giving the illusion of a stored property behaviour instead.
29/03 — Update
As outlined by Paolo, even if the above approach is working, it should be reviewed at least in two parts in order to solve the following:
- Private scoping for the Holder struct (actually, everyone can access the service static Structs changing the value without further controls).
- Multiple instances of the extended class. The proposed approach suffer of a very nasty problem: if you create multiple instances of the extended class, every instance will share the same static value from the Holder struct. The problem is related to the nature of the static keyword: Every instance of the extended class will share the same static value from the Holder struct. We can use a little bit of imagination in order to be creative and solve the issue.
The following should do the trick:
Here, we exploited some sort of hash table in order to hold all the possible different values belonging to different class instances. Please note that self.debugDescription is a really “raw” approach and we should elaborate a more concise and smart way to distinguish between different references of different classes. But, in any case, it seems working as a poc.
30/03 — Update
I couldn’t imagine that there was a so huge interest on this topic! Many thanks to all. There is still a margin of improvement in this approach and after a very kindly discussion with my friend Davide, I decided to give a follow up to the article in order to include some of his suggestions as well as to improve the hash table. Since we use a static var to emulate an hash table, there is no reason to still rely on the Struct approach. Agree. Definitely, this will save a lot of lines of code. Also, I would like to improve the hash table key mechanism by using memory address like pointers. The new version of the approach is as follow:
Much, much better! …there is still a minor issue regarding “memory leaking”…but this is another story…
Happy swifty coding!