I recently found a problem with Category in plugin environment. It was due to power of Objective-C. However, we need to put restriction on its functionality when necessary.
When we use Category in our own program, it is very powerful. Category allows to overriding some messages of classes we didn’t write. With C++, you should create a child class of such parent class and override some methods of the parent class. However with Objective-C, you just write messages which override existing messages at the level of the super class.
Now, let’s consider a hosting program which loads its plugins or frameworks you write. In plugins’ or frameworks’ codes, you defines a category which override some existing classes’ messages. You can safely guess that every lines of codes which calls the same message in your plugins/frameworks codes, they will use your version of the messages. Now, how about the host program? I thought that host program is separated from plugins/frameworks physically, when some codes in the host program calls the same messages, I didn’t expect that it would call my version of messages. What I expected was that the host program code will call original messages or if it overrides those messages for itself, then it will call its overridden messages.
However, my guess was wrong. Apple’s document mentiones that plugins/frameworks’ codes are to be loaded onto the same memory sapce as the host program.
It also stated that if you have multiple plugins/frameworks for each of them to override same messages, the runtime doesn’t guarantee what will be called first. What is called can be different from invocation to invocation.
However, this description cleverly get away from very important and crucial behaviour. If you once know the problem, you will recognize suddenly that the description can be understood such a way, but if you don’t know the problem, the setence doesn’t say anything about the dangerous behaviour.
So, here I would like to suggest adding a few keywords to Category declaration or interface.
1. keyword-based Approach
@interface(unified) ExistingClassName ( CategoryName ) ... @end
The (unified) means that the category is to be effective in memory space of the host program. If there is no (unified) stated, the overriding by category is to be restricted to the specific plugin codes only. It should not affect other plugins. Alternative form can be
@interface(unified=true) ExistingClassName ( CategoryName ) ... @end @interface(unified=false) ExistingClassName ( CategoryName ) ... @end
@interface(unified) ExistingClassName ( CategoryName ) ... @end @interface(nonunified) ExistingClassName ( CategoryName ) ... @end
On host side, there should be some project setting or Runtime environment configuration function to enable and disable if the specific program you write would allow its plugins category can be effective in memory space of the host program or not. Then we have some combinations.
|Host Program||Plugin Code||Category Behaviour|
2. Registration/Contract-based approach
In addition to the first approach, it should be able to negotiate between a host program and the plugin program about whether to allow “unified” behaviour or not.
This is necessary when a host program set “non-unified” mode. If “unified” mode is set for the host program, it is the host program writer’s responsibility to write such “open” host program.
The scope of this approach is, therefore, when a host program is set to non-unified mode, and depending on some agreed-upon keys or something like security descriptor, it is possible for a plugin to work in “unified” mode. It is a plugin which asks a host to allow “unified” mode for the plugin. (but not for other plugins)
In the plugin which obtained the privilege, you can freely define category and it can affect the memory space of the host program. Whether this is in effect for other plugins or not is in question. However, at least, it should be in effect for the host program. This is to allow ingest legible codes into the host program for maximum flexibility.
How is my proposal?