Defending programming with resolveInstanceMethod
Migrate db into a new one, we normally need a new class for the entity object, and so entirely all new properties. It seems unavoidable to miss some cases where a new object accesses properties of the old one. So how to avoid crashing ?
Working for Zalo, an app with more than 10 million active users, this is a crucial issue need to be solved.
As the title of the article, I use resolveInstanceMethod, a second chance to handle the exception when there is no such Sel of the class. Initially, the idea is to provide the generic dynamic implementation for all methods (mainly object return type, number return type and void method). Because object return type can see 0 as null, void method can be replaced as a method which returns a number, we just need to support number return type. Here is the code :
But life is not that easy. This implementation quickly crashes somewhere else. The reason is that wherever implement valueForKey, it first calls class_getInstanceMethod, and if objc_runtime was unable to find such methods, resolveInstanceMethod is called. Because valueForKey not just finds the implementation for key, but also _key, getKey .. so instead of getting return No from resolveInstanceMethod for the unsupported cases, it get nil or zero value, easily causing error or crash.
From here, I think of listing all cases that need to make an exception. To make things easy, I divided into three parts : dynamic resolve for integer, object and void. And list all the methods that need to be resolved, and latter write an assert for debug running.
Look easy ? But it takes me nearly half a day to complete this stuff.