Synthaway: A Property Synthesis Refactoring Tool from Inkling
Objective-C has changed a lot over the years. For a long time, the concept of “properties”, found in other languages, did not exist. That changed in 2006 with the introduction of declared properties as part of the Objective-C 2.0 standard. This new syntax allowed more formalized access to object attributes, though it did, however, still require either manual declaration or use of the “@synthesize” directive.
An improvement to the Clang compiler in 2012 dropped the requirement of the “@synthesize” directive, making it possible to simply declare properties and start using them without any additional code. This is a great time-saving improvement, but it leaves us with countless lines of legacy code cluttered with unnecessary @synthesize directives. Here at Inkling, we determined that it would be prohibitively time-consuming and error-prone to update our codebase by hand, so we looked for a better way.
We considered using regular expressions to remove the redundant directives but dismissed that idea for two reasons:
- Not all @synthesize directives can be removed. For example, if a readwrite property has both its getter and setter manually defined, the @synthesize directive is still needed so that the compiler knows to create the ivar for you. Also, properties defined within an Objective-C protocol require the directive.
- To take advantage of auto synthesis, ivars must be named in a new convention with the underscore prefix (e.g. “_image”). In many cases, legacy code does not follow this convention. For example, a common pattern has been to declare “@synthesize propertyName;” without specifically naming the ivar, which will result in an ivar named “propertyName” (without the underscore). These types of declarations can lead to code which is incompatible with auto-synthesized properties.
Regular expressions wouldn’t solve the problem, but we did come up with an idea that worked. We built a tool called “Synthaway“. Synthaway uses Clang’s abstract syntax tree (AST), which allows it to understand Objective-C code just as the compiler does. This is much more powerful and precise than simple string replacement. Since the tool has the semantic information associated with each property, it is able to perform intelligent actions, such as finding and renaming ivars which do not follow the proper conventions. This ensures that the refactored code can be successfully compiled while keeping the meaning intact, an important requirement for any refactoring tool. The use of Clang also enables Synthaway to handle both Objective-C and Objective-C++ files with the same level of precision.
Once we wrote and tested Synthaway, we let it loose on our code base. We were able to remove 77% of all @synthesize directives right away without any manual intervention. Doing this by hand would surely have taken longer and been much more error-prone. Besides, it’s much more fun to write new code than remove old code by hand!
By relying on the Clang lexer and AST hooks, we were able to keep Synthaway to just a single C++ source file under 500 lines. In the end, we all love clean code, and that’s the biggest reason why we chose to make Synthaway an open source project. We hope you find this tool useful not only in its current form but also as a template for writing future Clang-based tools. Clang has proven to be a very powerful and flexible toolchain, and we expect to see new creative uses as it becomes more widely adopted. We hope that anyone interested in writing a Clang-based tool will find Synthaway a good starting point.
Our dream is that Synthaway will eradicate a lot of redundant code out of the world’s Objective-C repositories. So do your part, and be a good code citizen by using Synthaway, giving us feedback, or even contributing pull requests.