Join CTO Moataz Soliman as he explores the potential impact poor performance can have on your bottom line. 👉 Register Today

ebook icon

App Development

General

Objective-C: Refactoring to Dynamism

“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

Why consider writing more dynamic code?

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.

What’s the catch?

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.

Examples

Scenario #1: An app requires comprehensive logging of all events performed on UIButtons; every touch, tap, etc., should be reported somewhere central.

Static code:


Dynamic code:


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.

Scenario #2: Parsing and storing JSON API that is expected to change.

Static code:


Dynamic code:



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.

Scenario #3: Checking if an API is authenticated without having a callback (delegate, block, etc.).

Static code:


Dynamic code:


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.

Scenario #4: Having multiple parents for a single class.

Dynamic code:


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.

Scenario #5: Handling different object types in the same UITableView. A backend API returns different kinds of media files that need to be handled and presented differently.

Static code:


Dynamic code:


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.

Conclusion

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.

NOW IT’S YOUR TURN: How do you feel about dynamism? Share your thoughts in the comments below.

Tags

No items found.

Start Your 14-Day Free Trial for Realtime Contextual Insights

In less than a minute, integrate the Instabug SDK for iOS, Android, React Native, Xamarin, Cordova, Flutter, and Unity mobile apps