There are situations in which it is better to write a category to a class than to use inheritance. It is also sometimes necessary to use the original class method within a category, as if we had called
super on inheritance. Sometimes people like to ask about such an opportunity at interviews and for some reason believe that this cannot be done. Google is also silent about this possibility. Who cares - welcome under cat.
Actually it all started with the support of the language with a letter from right to left of the old existing project. The first victim of the translation was the class
UITableView . After everything more or less began to work with inheritance, it was decided to apply the category for the subsequent quick addition of support for such letters to other projects.
The support of the letter from right to left was the re-division of some methods of the table. In this article, we consider the
initWithCoder: method .
First, let's write a category for
UITableView')
@interface UITableView (RTL) @end @implementation UITableView (RTL) - (id)initWithCoder:(NSCoder *)aDecoder{
We need to initialize the table with something, the most appropriate way is to call the original method
initWithCoder:. But as I was told earlier, this is impossible to do. I decided not to believe it, but to see what was going on in the class using the
Method * class_copyMethodList function (Class cls, unsigned int * outCount) . There I saw that the implementation of the predefined method occurs 2 times. Accordingly, if we get the addresses of these implementations and check what we are in, the other will be necessary for us to call. To find the implementation we need, the following class method has been written:
- (objc_methodPointer)methodBySelector:(SEL)sel{ unsigned int mc = 0; Class selfClass = object_getClass(self); Method *mList = class_copyMethodList(selfClass, &mc); Method currentMethod = class_getInstanceMethod(selfClass, sel); NSString *oldName = NSStringFromSelector(sel); Method oldMethod = NULL; for(int i = 0;i < mc; i++){ NSString *name = [NSString stringWithUTF8String:sel_getName(method_getName(mList[i]))]; if ([name isEqualToString:oldName] && currentMethod != mList[i]){ oldMethod = mList[i]; break; } } return oldMethod; }
Everything is simple, the selector for the search is passed to the function. From it we get the name and the current method. Next, we go through the list of methods and look for the implementation we need, checking the matching of the name and the mismatch of the methods.
Now we have a pointer to the method we need, but we still need to know its implementation. Fortunately, it is stored in the structure we received. But for some reason Apple decided not to put the full
Method structure in the blank files, although there is a description of it on developer.apple.com. By this all will have to describe themselves.
struct objc_method { SEL method_name; char *method_types; IMP method_imp; }; typedef struct objc_method *objc_methodPointer;
Now we have everything to call the original initialization method.
- (id)initWithCoder:(NSCoder *)aDecoder{
After assembling and running the application with this code, the table will be initialized initially. After that ours will be executed.
This code does not pretend to originality and ideality, this is the first version, which will be finalized in the coming days. (Even now I see that the copying of the list of methods is being called, but the memory is not released).
Thanks for attention.