There are some very powerful features in the Objective-C runtime, so is this something we should be taking advantage of more?
During my time as a developer I have seen my fair share of examples where people have abused the Objective-C runtime to solve a problem, when there was most likely a better solution.
One common example of this was when a developer uses objc_setClass() to change the class of an instance at runtime. I’ve seen this happen when developers want to instantiate a controller from a storyboard, but then subclass that class and go thorough the same initialiser.
This is all very straight forward, but what happens if you now want to subclass this function?
This will still return an instance of HorseViewController. We could then override the super method, and return our own instance. However this would mean duplicating the controller in the storyboard for our subclass.
This is where I have seen the Objective-C runtime used incorrectly to solve the issue above.
So why is this bad?
To really understand this we need to know a little about how the Objective-C runtime works, and how swift objects are converted to run inside this environment.
In essence, every object once compiled down to the Objective-C runtime, is a struct, which contains a single pointer called an isa, which is of type Class. That’s it… There isn’t really much to it. All I can say is that every object in the objc runtime has one of these pointers.
So what is an isa pointer?
The isa pointer is the first pointer-sized piece of memory that is allocated when initialising an object, and it contains information about the objects class. We can see this by looking at what makes up a Class.
Again a class is just a structure, and just like an object, it also has an isa pointer, which tells us that Class, is in-fact also an object. We also have a number of Objc2 only attributes.
super_class – this is a pointer to the objects parent. This is what is returned when we call super on an object. Notice there is no pointer to retrieve the subclasses.
objc_ivar_list – this is the list of instance variables for the current class. Pretty self explanatory. Same with objc_protocol_list – which is again just a list of the protocols a class conforms to. As these are both pointers they can be both changed out at runtime.
objc_method_list – is slightly different to the two above, as it is a pointer to a pointer, which allows the objc runtime to modify the classes methods.
objc_cache – the object cache is very important, as it is this that tells the build system if there is a cached version of the class already stored. When the runtime searches for an implementation which matches a selector, it will first check the cache to see if a value exists, and if it doesn’t, it will query the rest of the hierarchy.
name, version and info are just some metadata set on the class.
instance_size – is the amount of memory that class needs to be allocated.
Now we know more about what happens behind the scenes we can finally look at why changing the class at runtime could be a problem.
All we are doing when we call object_setClass is changing the isa pointer to the one of our new class. Our original object has already been initialised, and the correct amount of memory has been allocated. When we change the pointer we now have a new class which may need more memory, if it has more methods, ivars etc. This is where the problem lies. New methods may not have enough memory to execute so we could start to see random crashes appearing.
In my option if you want to share UI from a storyboard or xib, then I think the best approach would be to split it up into separate views or child view controllers. These can then easily be re-used in multiple view controllers.