Overriding / Swizzling methods from an existing shared delegate - objective-c

Is it possible to override ONLY CERTAIN functions from an exisiting delegate, without ourself being a delegate totally?
I tried replacing the target IMP with mine, didn't work :'(
More detail:
+[SomeClass sharedDelegate]
-[sharedDelegate targetMethodToBeOverridden:Arg:] //OUR method needs to be called, not this
Method *targetMethod; // targetMethodToBeOverridden identified by class_copymethodlist magic
targetMethod->method_imp = [self methodForSelector:#selector(overriddenDelegateMethod:Arg:)];
NOT WORKING! My Method is not being called :(

You probably shouldn't be manipulating the Method struct directly. Use the runtime function instead. You'll need to #import the runtime header, but there's a nice method in there called method_setImplementation. It'll work something like this:
id targetObject = [SomeClass sharedDelegate];
Method methodToModify = class_getInstanceMethod([targetObject class], #selector(replaceMe:argument:));
IMP newImplementation = [self methodForSelector:#selector(overriddenDelegateMethod:Arg:)];
method_setImplementation(methodToModify, newImplementation);
This may not work for your specific case, since class_getInstanceMethod might not return the Method for a method defined by an adopted protocol, but this is the "proper" way to swizzle Method IMPs.

Related

Initializing a constant that's value takes a completionBlock argument in its initializer

I have a property, that in Objective-C I created like this:
self.myProperty = [[MyClass alloc] initWithCompletionBlock:^(MyClass *object) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingAfterInitialization];
});
}];
And it worked great. Initialization of the MyClass object could create an indeterminate amount of time, so I passed a completionHandler in to it. When it finished, doSomethingAfterInitalization: would handle business.
Now in Swift, I'm trying to create the same object and assign it to a property, with problems.
The property never will change, so it makes sense to me to create it as a Swift constant.
So I'm trying it like this:
let myProperty = MyClass(completionBlock:{ (MyClass) -> (Void) in dispatch_async(dispatch_get_main_queue(), doSomethingAfterInitialization())})
To me that seemed like a direct translation... but the Swift compiler tells me that's not correct, via the error
Use of instance member 'doSomethingAfterInitialization' on type 'MyViewController'; did you mean to use a value of type 'MyViewController' instead?
Well that didn't help much. So instead I tried changing the call to the doSomethingAfterInitialization function to self. doSomethingAfterInitialization(), in which case I see
Value of type '(NSObject) -> () -> TodayWidgetTableViewController' has no member 'doSomethingAfterInitialization'
Any idea how I can fix this? Obviously my initializer is a little weird in the first place, so I'm wondering if this is something that doesn't really translate at all to Swift.

Call class method of Objective C category

AFNetworking has a class method called + af_sharedImageCache that I want to access from my own category, but I can't figure out the syntax to reference this. Internally it is doing [[self class] af_sharedImageCache] but obviously that's not gonna work for me. :)
That's because + af_sharedImageCache is a private method, not exposed on UIImageView+AFNetworking.h. You can call it, though, using Obj-C runtime.
if ([[self class] respondsToSelector:#sel(af_sharedImageCache)]) {
NSCache *cache = [[self class] performSelector:#sel(af_sharedImageCache)];
}
However, AFImageCache is a private class, and you would have to do the same hack to use its methods. If I were you, I'd create my own cache, as it's clear that AFNetworking doesn't want that you mess with its cache implementation.

Objective C custom class methods not being called

So I have this custom class with just a test method that does nslog. I am going to reuse this method many times in my app. The interface looks like this.
#interface TestViewController: UIViewController { CMImageMover * imageMover }
Then in the view did load I:
imageMover = [[CmImageMover alloc] init];
If I do:
[imageMover testMethod];
Right after the alloc and init it works in the viewDidLoad function but if I call it again from another function in the view controller nothing works and the class method does not get called.
What am I doing wrong here. Every other var I declare like NSArray/NSTimer, I do the say way and I am able to access and use it throughout my controller.
When you say "if I call it again from another function in the view controller nothing works" then first thing to check is what you are sending the testMethod. It could be nil, in which case nothing will happen. In objective C sending a message to nil does nothing. Add an NSLog to find out, e.g.
NSLog(#"imageMover object is: %#", imageOver);
[imageMover testMethod];
If the NSLog shows it is nil - or something crazy - then follow up what you are doing with the imageMover ivar.
You mention a class method in your question, but don't refer to it in your code snippets.
If you have defined testMethod as a class method it will, of course, fail if you send that message to an instance. (And it will fail noisily.) A class method would be introduced like this:
+ (void) testMethod
{
NSLog(#"CMImageMover testMethod called on Class");
}
An instance method would be introduced like this:
- (void) testMethod
{
NSLog(#"testMethod called on an instance of CMImageMover");
}
Apologies if this is all screamingly obvious to you and missing the point of the question. It's not that clear from your question where the issue lies.

Selectors in Cocos2d schedule method

So I am doing this to initialize my selector:
//In .h
SEL selectors[3];
//In .m
selectors[0] = #selector(rotate);
selectors[1] = #selector(discharge);
And here is the problem:
When I call this in my init method in Cocos2d like this:
[self performSelector:selectors[0]];
it works fine, but when I call this line of code in a method called moveThings which is invoked through the schedule ([self schedule:#selector(moveThings:)]) at the end of my init method in Cocos2d it gives EXC_BAD_ACCESS. What is the problem with scheduling things?
UPDATE:
I have found there is a problem with the rotate function (the function being stored in selector[0]). Here it is:
-(void)rotate:(ccTime)delta {
if (((CCSprite *)[creature objectAtIndex:0]).rotation < 360) {
((CCSprite *)[creature objectAtIndex:0]).rotation++;
}
else {
((CCSprite *)[creature objectAtIndex:0]).rotation++;
}
}
If I comment the contents of the method out it works fine when called through moveThings and init.
If I change the methods contents with:
((CCSprite *)[creature objectAtIndex:0]).rotation++;
It fails... But, again, I would like to state that all of these things do work if I call it in my init method, even call it twice in a row, but it will not work (except when I take out the contents of the rotate method) if I call it through the moveThings: method which is being invoke through the schedule method it fails.
Further update:
If I call:
((CCSprite *)[creature objectAtIndex:0]).rotation++;
In moveThings (which is being, as I've said before, invoked by the schedule:(SEL) method) it fails. Where as long as it is not invoked through a method that is the called by schedule it works.
The problem is that when you call performSelector there are only two options:
have your selector take no arguments and leave the ":" off the #selector(foo) definition.
have your selector take either one or two arguments which both must be an NSObject or subclass
it is the latter that is messing you up here I suspect.
Here are the three forms of performSelector:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
You'll note that the withObject arguments are all of type id which is an Objective C object.
The selector you're trying to use takes a ccTime which is a float and not an Objective C object as it's param and that is why things are crashing:
-(void)rotate:(ccTime)delta;
One option is to make a wrapper method that takes a wrapped ccTime and unwraps it and calls the rotate:(ccTime) method:
- (void) wrappedRotate: (NSNumber*) inDelta
{
[self rotate: [inDelta floatValue]];
}
then use
selectors[0] = #selector(wrappedRotate:);
and then call via:
[self schedule: #selector(moveThings:)]); // cocos2d schedule
...
- (void) moveThings: (ccTime) dt
{
[self performSelector: selectors[0] withObject: [NSNumber numberWithFloat: dt]];
...
}
One reason you are getting confused is because Cocos2d is using #selector in somewhat more complicated ways (see CCScheduler.m in the CCTimer::initWithTarget:selector:interval: and CCTimer::update: method in particular).
disclaimer: code typed into SO so not checked with a compiler, but the essence of what you need should be here.
One problem for sure is that you are using a variable declared inside a .h while initializing it inside the relative .m. According to the linking I'm not sure that just one variable selectors will exist (so that different files that include .h will have different versions).
First of all I suggest you to try adding the keyword extern to have
extern SEL selectors[3];
to tell your linker that it is initialized inside the relative .m and to use just that one.
I think your problem stems from your method definition which is - (void)rotate; and not - (void)rotate:(ccTime)dt;
You should adjust your selectors likewise.
If your method does not have any arguments then do not use a colon in your selector call.
// Requires #selector(foo:)
- (void) foo:(id)sender;
// Requires #selector(foo)
- (void) foo;

Check if a method exists

Is there any way I can test if a method exists in Objective-C?
I'm trying to add a guard to see if my object has the method before calling it.
if ([obj respondsToSelector:#selector(methodName:withEtc:)]) {
[obj methodName:123 withEtc:456];
}
There is also the static message instancesRespondToSelector:(SEL)selector
You would call it like this:
[MyClass instancesRespondToSelector:#selector(someMethod:withParams:)]
or like this:
[[myObject class] instancesRespondToSelector:#selector(someMethod:withParams:)]
This may be useful if you would like to call one constructor or another one depending on this (I mean, before having the instance itself).
Use respondsToSelector:. From the documentation:
respondsToSelector:
Returns a Boolean value that indicates whether the receiver implements or inherits a method that can respond to a specified message.
- (BOOL)respondsToSelector:(SEL)aSelector
Parameters
aSelector - A selector that identifies a message.
Return Value
YES if the receiver implements or inherits a method that can respond to aSelector, otherwise NO.
You're looking for respondsToSelector:-
if ([foo respondsToSelector: #selector(bar)] {
[foo bar];
}
As Donal says the above tells you that foo can definitely handle receiving the bar selector. However, if foo's a proxy that forwards bar to some underlying object that will receive the bar message, then respondsToSelector: will tell you NO, even though the message will be forwarded to an object that responds to bar.
Checking selectors with respondsToSelector is normally only for delegate methods. You shouldn't be using forwardInvocation or proxies for delegate methods. If you need to use respondsToSelector in other situations you might want to make sure that there isn't a more appropriate way to design your program.