“Instead of this absurd division into sexes they ought to class people as static and dynamic.”
- Evelyn Waugh
The standard Objective-C introduction usually mentions its Smalltalk heritage along with its strict C dependence. So how can a language running over the hallmark of procedural programming be dynamic? The Objective-C runtime. It’s the only dynamic library that doesn’t have to be included in an Xcode project’s frameworks, as it is loaded automatically by the OS. It allows for object orientation, dynamic and duck typing, messaging, protocols, introspection, etc. It simply puts the objective in Objective-C.
"The Objective-C language defers as many decisions as it can from compile time and link time to runtime. Whenever possible, it does things dynamically. This means that the language requires not just a compiler, but also a runtime system to execute the compiled code. The runtime system acts as a kind of operating system for the Objective-C language; it’s what makes the language work."
- Objective-C Runtime Programming Guide
Write less code: Objective-C is a verbose language that loves boilerplate code, and while that's not always a bad thing, it can get in the way of getting things done. When Swift was introduced, a lot of the excitement was exclusively over how much could be accomplished with so little code. Writing less Objective-C code is appealing not only because it’s less work -- even though one should definitely “work smart, not hard” -- but also because it means reducing cost of maintenance and change.
Write more SOLID code: The open/closed principle (OCP) urges developers to write "software entities" that are open to extension but closed for modification. While this issue is usually addressed in classes (with subclassing), methods are also entities that should ideally be stabilized. Not, for example, by adding a new conditional statement every time a new file type is introduced to a class that handles files.
Implement design patterns more easily: A computer scientist found that 16 out of the 23 design patterns found in Gang of Four’s Design Patterns (the Gray's Anatomy of OOP design books) are invisible or simpler in dynamic languages. So it’s a better, more fit tool for the job.
Have more fun: Writing dynamic code is just more fun! Having less limitations, writing less control flow, less casting, etc., just makes writing code a more elegant and less repetitive process.
Runtime overhead: With all the power the Objective-C runtime provides, sometimes the effect is cost of performance. For example, the dynamic resolution of a selector is often a more expensive operation than calling the same method directly, due to compiler optimizations. That being said, unless it’s a very resource intensive app/game and every millisecond counts, the boost in code quality and developer productivity outweighs the minuscule performance hit.
Safety: Dynamic code, by design, has fewer compiler checks, as it is evaluated at runtime, which means it also fails at runtime. So any code path or environment configuration that is not tested might have a nasty bug that would only present itself in product, a less than ideal situation. This would be even more obvious in “stringly-typed” features like KVC, KVO, and selectors.
Code readability: While this might be subjective, dynamic code is usually less self-documenting than its static counterpart. The run flow might be less pronounced than a familiar if-else structure, at least at first glance.
Static approach has the apparent disadvantage of introducing a new subclass that requires changing every class that has button code; it also fails to solve the problem all together if third party code is used, as it cannot be changed to accommodate the new subclasses. Another downer is that said solution cannot be generalized on all controls to pickers, switches, etc.
The dynamic solution is much more plug and play; it swizzles all sendAction:to:from:forEvent: messages to a category (more on that later) and calls a custom method that does the logging after. Swizzling would work on all subclasses of UIControl, even third party code. The technique has endless applications, from mocking objects for testing to overriding behaviors (monkey-patching) in Cocoa Touch or third-party code beyond what is allowed in its public API.
Static approach both requires knowledge of returned JSON keys and needs to configure corresponding properties in the model class. Also, whenever the the returned JSON structure changes, the model would need to be updated, tested, and deployed again, which is less than ideal.
The disadvantages of this should be apparent. The dynamic approach is worlds apart in terms of flexibility; it adapts to the API the way a model should, and responds to getters and setters the way properties should. This can be the starting point to creating a full-fledged dynamic model layer à la Github’s Mantle from scratch.
Static approach not only exhibits the usual ugliness, but is also prone to error; if authentication takes longer than one second, it would just fall apart. KVO on the other hand, while still not pretty, would only get called when the value of isUserAuthenticated is changed. KVO is best suited to create event-driven implementations and binding model to view and can be a good introducing to functional programming with ReactiveCocoa, Swift etc.
A static approach was simply not possible in Objective-C; it was never intended to support multiple inheritance. The dynamic solution comes from and uses ObjectiveMixin open source implementation. It’s a very compact (~100 LoC) class that uses the Objective-C runtime to copy method implementations from one class to another, thus allowing pseudo multiple parents to a single class, similar to Ruby’s mixins. The fact that this is also done at runtime allows dynamic subclassing on the spot, after classes are compiled. This scenario demonstrates best how dynamism can totally change the patterns and paradigms used to write iOS/Mac solutions.
The static approach should look very familiar to most developers, and it’s not “wrong” by any means. Actually, calling it static is slightly misleading because it does use introspection feature of the runtime (isKindOfClass:), but the dynamic approach is still a little better. Not only does it offer a lower cyclomatic complexity (the number of independent code paths), which in turn increases code readability, but it makes the tableView:cellForRowAtIndexPath: method more open and closed in the OCP sense as well. It doesn’t need to be modified when handling a new object type later on.
This is not great entry-level material for learning the runtime APIs, but is valuable for engineers who are comfortable with the tools yet want to polish their software design. The examples in this article are not meant to explain features as much as to advocate a paradigm shift, thinking more of dynamism and metaprogramming while designing the little things in code.
This only touches the surface of what one can do with Objective-C using rather than simple runtime APIs. Features like introspection, posing, dynamic method resolution and forwarding, dynamic class generation, ISA swizzling, and much more weren’t even mentioned. The runtime opens a lot of possibilities that were simply not feasible by just the language syntax, and it helps create a sexier, more-maintainable code for the next generation.
- How We Migrated Our Front-End While Scaling at Instabug
- Why We Automated Our Front-End Testing at Instabug
- Mobile App Testing Vs. Mobile SDK Testing: What is the Difference?
- How We Implemented View Hierarchy In Instabug
Instabug empowers mobile teams to maintain industry-leading apps with mobile-focused, user-centric stability and performance monitoring.