The Objective-C Runtime
Objective-C has surprisingly strong runtime reflection capabilities, on par with that of JavaScript. Here's a couple ways you could do it with the Objective-C runtime library.
Option 1: Register and deregister classes
Objective-C lets you create new classes at runtime with objc_allocateClassPair, and deregister existing classes with objc_disposeClassPair. Whenever a class needs to be reloaded, you could register the new version as a new class and dispose the existing one. The problem with this approach is, as per the documentation for objc_disposeClassPair:
Do not call this function if instances of the cls class or any subclass exist.
However, you can change the class of an object at runtime with object_setClass. Therefore, you could still use this approach with the following process:
- Allocate, configure, and register the new version of the class.
- Iterate over all instances of the old version of the class, and change them to the new version. Objective-C doesn't have an "all instances" function in its runtime, so this would have to be a static property on the class.
- Dispose the old version of the class.
Option 2: Mess with the class metadata
This allows you to work without having to worry about subclasses or iterating over existing instances, but may be much more involved. You'd have to check each part of the definition individually.
Methods
To change the implementation of an existing method, use method_setImplementation or method_exchangeImplementations. This is a well-supported pattern known as method swizzling; as I mentioned in a comment, this is usually used for dependency injection.
To add new methods, use class_addMethod.
Removing methods is actually not something that's not supported! If you're making a new language, you could probably add support for this in your language, but Objective-C doesn't have it. However, there is a way for an object to pretend like it doesn't have a method, even at compile time, so we can use the same technique here.
The first step is to write the method implementation as follows:
- (void)exampleMethod {
[self doesNotRecognizeSelector:_cmd];
}
doesNotRecognizeSelector: is the method invoked by the runtime when all attempts to find the implementation of a method have failed. _cmd is an implicit argument that all methods take, which is the method's selector (a representation of the method name).
The second step is to make respondsToSelector: and instancesRespondToSelector: also deny it:
- (BOOL)respondsToSelector:(SEL)aSelector {
if (sel_isEqual(@selector(exampleMethod), aSelector)
return NO;
return [super respondsToSelector:aSelector];
}
+ (BOOL)instancesRespondToSelector:(SEL)aSelector {
if (sel_isEqual(@selector(exampleMethod), aSelector)
return NO;
return class_respondsToSelector(self, aSelector);
}
Then, you can drop these in with the functions mentioned above.
Properties
Properties are the public interface used to access instance members, while ivars are the underlying storage for them. Changing properties is fairly straightforward: class_replaceProperty to change an existing one and class_addProperty to add a new one, while deleting old ones is basically in the same boat as deleting methods. Each property corresponds to a pair of selectors (e.g. foo for the getter and setFoo: for the setter).
Ivars
This is where things get interesting. Messing with ivars means changing the actual memory layout of the class. Surprisingly, this is just supported: class_setIvarLayout for owned ivars and class_setWeakIvarLayout for weak ones. You may have to take care that existing instances don't become dangerous when you do this.
Protocols
Protocols are Objective-C's equivalent of Java's interfaces. Once again, adding new ones is supported but removing existing ones is complicated. Adding a new one is just a call to class_addProtocol plus implementing the necessary methods.
You can't remove the protocol from the class list, but you can make the class act like it's not there by overriding conformsToProtocol:
+ (BOOL)conformsToProtocol:(Protocol *)protocol {
if ([protocol isEqual:@protocol(ProtocolToRemove)])
return NO;
return class_conformsToProtocol(self, protocol);
}
sun.misc.unsafe?) it would make for a good answer. $\endgroup$