Swizzling a single instance, not a class - objective-c

I have a category on NSObject which supposed to so some stuff. When I call it on an object, I would like to override its dealloc method to do some cleanups.
I wanted to do it using method swizzling, but could not figure out how. The only examples I've found are on how to replace the method implementation for the entire class (in my case, it would override dealloc for ALL NSObjects - which I don't want to).
I want to override the dealloc method of specific instances of NSObject.
#interface NSObject(MyCategory)
-(void)test;
#end
#implementation NSObject(MyCategory)
-(void)newDealloc
{
// do some cleanup here
[self dealloc]; // call actual dealloc method
}
-(void)test
{
IMP orig=[self methodForSelector:#selector(dealloc)];
IMP repl=[self methodForSelector:#selector(newDealloc)];
if (...) // 'test' might be called several times, this replacement should happen only on the first call
{
method_exchangeImplementations(..., ...);
}
}
#end

You can't really do this since objects don't have their own method tables. Only classes have method tables and if you change those it will affect every object of that class. There is a straightforward way around this though: Changing the class of your object at runtime to a dynamically created subclass. This technique, also called isa-swizzling, is used by Apple to implement automatic KVO.
This is a powerful method and it has its uses. But for your case there is an easier method using associated objects. Basically you use objc_setAssociatedObject to associate another object to your first object which does the cleanup in its dealloc. You can find more details in this blog post on Cocoa is my Girlfriend.

Method selection is based on the class of an object instance, so method swizzling affects all instances of the same class - as you discovered.
But you can change the class of an instance, but you must be careful! Here is the outline, assume you have a class:
#instance MyPlainObject : NSObject
- (void) doSomething;
#end
Now if for just some of the instances of MyPlainObject you'd like to alter the behaviour of doSomething you first define a subclass:
#instance MyFancyObject: MyPlainObject
- (void) doSomething;
#end
Now you can clearly make instances of MyFancyObject, but what we need to do is take a pre-existing instance of MyPlainObject and make it into a MyFancyObject so we get the new behaviour. For that we can swizzle the class, add the following to MyFancyObject:
static Class myPlainObjectClass;
static Class myFancyObjectClass;
+ (void)initialize
{
myPlainObjectClass = objc_getClass("MyPlainObject");
myFancyObjectClass = objc_getClass("MyFancyObject");
}
+ (void)changeKind:(MyPlainObject *)control fancy:(BOOL)fancy
{
object_setClass(control, fancy ? myFancyObjectClass : myPlainObjectClass);
}
Now for any original instance of MyPlainClass you can switch to behave as a MyFancyClass, and vice-versa:
MyPlainClass *mpc = [MyPlainClass new];
...
// masquerade as MyFancyClass
[MyFancyClass changeKind:mpc fancy:YES]
... // mpc behaves as a MyFancyClass
// revert to true nature
[MyFancyClass changeKind:mpc: fancy:NO];
(Some) of the caveats:
You can only do this if the subclass overrides or adds methods, and adds static (class) variables.
You also need a sub-class for ever class you wish to change the behaviour of, you can't have a single class which can change the behaviour of many different classes.

I made a swizzling API that also features instance specific swizzling. I think this is exactly what you're looking for: https://github.com/JonasGessner/JGMethodSwizzler
It works by creating a dynamic subclass for the specific instance that you're swizzling at runtime.

Related

How to allocate an NSObject subclass instance FROM an instance of its superclass?

Given a class structure such as...
#interface SuperClassView : NSView #end
#interface SubClassedView : SuperClassView #property int aProp; #end
How can one instantiate a SubClassedView from an instance of a SuperClassView?
Say a method returns an instance of the superclass SuperView....
SuperClassView *superInstance = [ViewFactory makeSuperClassView];
but I want to get an instance of the subclass SubClassedView? It is not possible to simply "cast" it...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
and there is no built-in (or easily-imagined implementation of an) NSObject method like
self = [super initWithInstance:[superInstance copy]];`
Is the only way to either copy the superclass instance's desired properties to the newly instantiated subclass object, like...
SubClassedView *subClsInstance = SubClassedView.new;
for (NSString* someKey in #["frame",#"color",#"someOtherProperty])
[subClsInstance setValue:[superInstance valueForKey:someKey] forKey:someKey];
Or add (swizzle at runtime) the subclass' "additional property methods" (in this case setAProp: and aProp) to the superclass instance (and also cast it upwards)...
SubClassedView *subClsInstance = (SubClassedView*)[ViewFactory makeSuperClassView];
[subClsInstance addSwizzleMethod:#selector(setAProp:) viaSomeMagic:....];
[subClsInstance addSwizzleMethod:#selector(aProp) viaSomeMagic:....];
Hopefully this is an easy runtime trick that I simply don't know... not a sad sign that I am haplessly trying to trick ObjC into multiple-inheritance via some embarrassing anti-pattern. Either way, ideas?
EDIT: Pending #BryanChen posting his comment as an answer this is achieved easily via his suggested runtime function, or as a category on NSObject á la..
#implementation NSObject (SettingClass)
- (void)setClass:(Class)kls { if (kls) object_setClass(self, kls); } #end
What you are trying to do is pretty non-idiomatic... it feels like you are trying to do something like prototype based OOP. A couple of quick points:
Don't do the swizzle. You can't swizzle onto an instance, you swizzle onto the class definition, so if you do that you won't be adding the subclasses methods onto "an" instance of the superclass, you will be adding them onto all instances of the superclass.
Yes, if you want to do this you just need to copy the the properties you want from the super into the new instance of the subclass.
You can have a factory method in the superclass that returns a subclass, and encapsulate all the the copying in there (so, -[SuperClassView makeSubclassView] that returns SubClassedView *. That is actually relatively common, and is how many of the class clusters are implemented (though they return private subclasses that conform to the implementation of the superclass)
object_setClass is not the droid you're looking for.
Yes, it will change the class of the instance. However, it will not change the size of it. So if your SubClassView declares extra properties or instance variables that are not declared on SuperClassView, then your attempts to access them on this frankenstein instance will result in (at best) buffer overflows, (probably) corrupted data, and (at worst) your app crashing.
It sounds like you really just want to use self in your factory method:
+ (instancetype)makeView {
return [[self alloc] init];
}
Then if you call [SuperClassView makeView], you get back an instance of SuperClassView. If you call [SubClassView makeView], you get back an instance of SubClassView.
"But," you say, "how do I customize the properties of the view if it's a SubClassView?"
Just like you would with anything else: you override the method on SubClassView:
#implementation SubClassView
+ (instancetype)makeView {
SubClassView *v = [super makeView];
v.answer = 42;
return v;
}
#end
object_setClass may or may not be the "runtime trick" you are looking for. It does isa swizzle which change the class of an instance at runtime. However it does have many constrains such as that the new class cannot have extra ivars. You can check this question for more details.
I think the better way to do is that instead of making view using [ViewFactory makeSuperClassView], make it [[SuperClassView alloc] initWithSomething]. Then you can do [[SubClassView alloc] initWithSomething]
or if you really want use ViewFactory, then make it [ViewFactory makeViewOfClass:]

Can subclass override non-public methods

I have two classes: BatchDownloader, SpeechDownlader
BatchDownloader is the base class, and SpeechDownloader inherited it.
In BatchDownloader, whenever one file was downloaded, -(void)downloadComplete:task will be called.
But in SpeechDownloader, I also want to post a notification in downloadComplete:task.
Can I just write the method with the same name in SpeechDownloader's implementation ? or there is a better way ?
Thanks.
p.s. I don't want to make -(void)downloadComplete:task public, because it should only be called by itself.
If you implement a method in a subclass that has the same name as a private method in a superclass, your subclass method will be called on instances of your subclass.
i.e., if you implement a method in your superclass like this, without declaring it anywhere:
#implementation classA
- (void)doSomething {
NSLog("a");
}
Then, in your subclass implementation, implement a method with the same name:
#implementation subclassOfA
- (void)doSomething {
NSLog("b");
}
When you call doSomething on an instance of your subclass, the subclass implementation will be called instead of the superclass implementation, so the code in this example will result in "b" being printed to the console.
However, if you also want to access the superclass implementation of the method, you can use:
- (void)doSomething {
[super doSomething];
NSLog("b");
}
This will also call the superclass implementation. If you get a compile error (due to the method being private and super not appearing to implement it), you can use [super performSelector:#selector(doSomething)] instead to do exactly the same thing.
This happens because of the way the Objective-C runtime looks up method calls. Since these methods have exactly the same method signature (same name, return type and arguments [none]), they are considered equal, and the runtime always checks the class of the object before looking in superclasses, so it will find the subclass method implementation first.
Also, this means you can do this:
classA *test = [subclassOfA new];
[test doSomething];
And, surprise surprise, the console will print "b" (Or "a b" if you called the super implementation too).
If you implement the method with the same method signature it will be called faith your implementation, public or not.

How to avoid subclass inadvertently overriding superclass private method

I'm writing a library, which will potentially be used by people that aren't me.
Let's say I write a class:
InterestingClass.h
#interface InterestingClass: NSObject
- (id)initWithIdentifier:(NSString *)Identifier;
#end
InterestingClass.m
#interface InterestingClass()
- (void)interestingMethod;
#end
#implementation InterestingClass
- (id)initWithIdentifier:(NSString *)Identifier {
self = [super init];
if (self) {
[self interestingMethod];
}
return self;
}
- (void)interestingMethod {
//do some interesting stuff
}
#end
What if somebody is using the library later down the line and decides to create a subclass of InterestingClass?:
InterestingSubClass.h
#interface InterestingSubClass: InterestingClass
#end
InterestingSubClass.m
#interface InterestingSubClass()
- (void)interestingMethod;
#end
#implementation InterestingSubClass
- (void)interestingMethod {
//do some equally interesting, but completely unrelated stuff
}
#end
The future library user can see from the public interface that initWithIdentifier is a method of the superclass. If they override this method, they'll probably assume (correctly) that the superclass method should be called in the subclass implementation.
However, what if they define a method (in the subclass private interface) which inadvertently has the same name as an unrelated method in the superclass 'private' interface? Without them reading the superclass private interface, they won't know that instead of just creating a new method, they've also overridden something in the superclass. The subclass implementation may end up getting called unexpectedly, and the work that the superclass is expecting to be done when calling the method will not get done.
All of the SO questions I've read seem to suggest that this is just the way that ObjC works and that there isn't a way of getting around it. Is this the case, or can I do something to protect my 'private' methods from being overridden?
Alternatively, is there any way to scope the calling of methods from my superclass so I can be sure that the superclass implementation will be called instead of a subclass implementation?
AFAIK, the best you can hope for is declaring that overrides must call super. You can do that by defining the method in the superclass as:
- (void)interestingMethod NS_REQUIRES_SUPER;
This will compile-time flag any overrides that don't call super.
For framework code a simple way to deal with this is to just give all of your private methods a private prefix.
You'll often notice in stack traces that the Apple frameworks call private methods often starting with an under bar _.
This would only really be a real concern if you are indeed providing a framework for external use where people can not see your source.
NB
Don't start your methods with an under bar prefix as this convention is already reserved

Exposing/Synthesizing iVar properties in Objective c

I have a class that essentially acts as a light weight wrapper class around another class. It holds that other class as an iVar. I want to be able to expose certain properties (quite a few actually) of the iVar, but to do so I have to write out each property accessor like so:
- (void) setProperty:(Class *)value{
_iVar.property = value;
}
- (Class *) property{
return _iVar.property;
}
Of course, I have to do this for every single property, which is a pain (there are about 30 of them). I would love to be able to synthesize this but I haven't been able to figure out how.
Is it possible to synthesize?
Also, I can't subclass....well, I might be able to but it's really not recommended. The iVar class is really quite heavy (it implements CoreText). I'd rather write out the methods by hand.
Ok, so here's the solution I found...ended up being pretty simple once you knew what to do. First overwrite '- (id) forwardingTargetForSelector:(SEL)aSelector' and return the iVar:
- (id) forwardingTargetForSelector:(SEL)aSelector{
return iVar;
}
When the runtime is looking for a method and cannot find one, it will call this method to see if there is another object to forward the message to. Note that this method normally returns nil and if you return nil here, your program will crash (which is the appropriate behavior).
The second part of the problem is to shush the compiler errors/warnings you'll get when you try to send a message that's not declared. This is easily done by declaring a category you don't implement.
#interface Class (iVarClassMethods)
#propoperty (strong) Class *property1;
......more properties
#end
As long as you don't put in an implementation anywhere, aka #implementation Class (category), the compiler won't complain (it'll assume that the implementation is somewhere....).
Now the only drawback I see is if you change any of the properties in the interface of the iVar Class, you need to make sure you update all other classes that use the method described above, otherwise you'll crash when another class tries to send what is now the wrong method (and the compiler won't warn you beforehand). However, this can be gotten around. You can declare protocols in a category. So instead you create a separate protocol for the iVar class and move the methods/properties you wish out of the iVar class into the protocol.
#protocol iVarClassProtocol
#propoperty (strong) Class *property1;
......more properties
#end
Add that protocol to the iVar subclass so it has those methods declared through the protocol now.
#interface iVarClass <iVarClassProtocol>
....other methods/properties you don't need forwarded
#end
Finally, simply add the protocol to the category. So instead of the aforementioned category with explicit declarations you'll have:
#interface Class (iVarClassMethods) <iVarClassProtocol>
#end
Now, if you need to change any of the to-be-fowarded properties/methods, you change them in the protocol. The compiler will then warn you when you try to send the wrong method to the forwarding class.
I think you can forward the messages to the ivar:
- (void) forwardInvocation: (NSInvocation*) invocation
{
[invocation invokeWithTarget:ivar];
}
- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
NSMethodSignature *our = [super methodSignatureForSelector:selector];
NSMethodSignature *ivars = [ivar methodSignatureForSelector:selector];
return our ? our : ivars;
}
Then you have to hide or fake the type of your object, for example by casting to id, otherwise the compiler will complain that your class does not implement those methods.
Of course it would be best if you could come up with some better design that would do without such tricks.

Override a method via ObjC Category and call the default implementation?

When using categories, you can override implementation methods with your own like so:
// Base Class
#interface ClassA : NSObject
- (NSString *) myMethod;
#end
#implementation ClassA
- (NSString*) myMethod { return #"A"; }
#end
//Category
#interface ClassA (CategoryB)
- (NSString *) myMethod;
#end
#implementation ClassA (CategoryB)
- (NSString*) myMethod { return #"B"; }
#end
Calling the method "myMethod" after including the category nets the result "B".
What is the easiest way for the Category implementation of myMethod to call the original Class A myMethod? As near as I can figure out, you'd have to use the low level calls to get the original method hook for Class A and call that, but it seemed like there would be a syntactically easier way to do this.
If you want a hackish way to do this that involves mucking with the objective-c runtime you can always use method swizzling (insert standard disclaimers here.) It will allow you to store the different methods as arbitrariliy named selectors, then swap them in at runtime as you need them.
From comp.lang.objective-C FAQ listing: "What if multiple categories implement the same method? Then the fabric of the Universe as we know it ceases to exist. Actually, that's not quite true, but certainly some problems will be caused. When a category implements a method which has already appeared in a class (whether through another category, or the class' primary #implementation), that category's definition overwrites the definition which was previously present. The original definition can no longer be reached by the Objective-C code. Note that if two categories overwrite the same method then whichever was loaded last "wins", which may not be possible to predict before the code is launched."
From developer.apple.com: "When a category overrides an inherited method, the method in the category can, as usual, invoke the inherited implementation via a message to super. However, if a category overrides a method that already existed in the category's class, there is no way to invoke the original implementation"
Check out my article about a solution found on the Mac Developer Library:
http://codeshaker.blogspot.com/2012/01/calling-original-overridden-method-from.html
Basically, it's the same as the above Method Swizzling with a brief example:
#import <objc/runtime.h>
#implementation Test (Logging)
- (NSUInteger)logLength {
NSUInteger length = [self logLength];
NSLog(#"Logging: %d", length);
return length;
}
+ (void)load {
method_exchangeImplementations(class_getInstanceMethod(self, #selector(length)), class_getInstanceMethod(self, #selector(logLength)));
}
#end
With the swizzling "helper" methods included in ConciseKit, you actually call the default implementation… weirdly enough.. by calling your SWIZZLED implementation..
You set it up in + (void) load, calling + (BOOL)swizzleMethod:(SEL)originalSelector with:(SEL)anotherSelector in:(Class)klass;, i.e.
[$ swizzleMethod:#selector(oldTired:)
with:#selector(swizzledHotness:) in:self.class];
and then in the swizzled method.. let's suppose it returns -(id).. you can do your mischief, or whatever reason you are swizzling in the first place… and then, instead of returning an object, or self, or whatnot..
return [self swizzledHotness:yourSwizzledMethodsArgument];
As explained here…
In this method, it looks like we're calling the same method again, causing and endless recursion. But by the time this line is reached the two method have been swapped. So when we call swizzled_synchronize we're actually calling the original method.
It feels and looks odd, but.. it works. This enables you to add endless embellishments to existing methods, and still "call super" (actually self) and reap the benefits of the original method's handiwork… even without access to the original source.