Replacing delegate methods that return values - objective-c

Using ReactiveCocoa is a cleaner and centralized way to handle events than delegate methods, however I was wondering if it is possible to replace even the methods that return values without losing its value.
In the example below, the method gestureRecognizer:shouldReceiveTouch: from UIGestureRecognizerDelegate is called, but the method signature expects a BOOL return value to be effective. This way, the gesture recognizer just don't work, as if the method returned NO
Is it possible to use RAC to replace this kind of method?
[[self rac_signalForSelector:#selector(gestureRecognizer:shouldReceiveTouch:)
fromProtocol:#protocol(UIGestureRecognizerDelegate)]
subscribeNext:^(id x){
NSLog(#"Was called, but how do I return the actual permission value?");
}];
self.backgroundTapGesture.delegate = self;

Is it possible to use RAC to replace this kind of method?
Nope. -rac_signalForSelector: cannot be used on selectors of non-existent methods, of non-void return types. In these cases, implement the method to return a desired value. This makes it an existent method, which -rac_signalForSelector: can be applied to.

Related

Call a method every time a parameter is set on Objective-C (Cocoa)

I currently have a class with 15 properties (and growing), and I'm finding myself having to call an update method every time one of those properties change.
Currently, I'm overriding every setter with a code like this:
-(void)setParameterName:(NSUInteger)newValue {
if (_param == newValue)
return;
_param = newValue;
[self redraw];
}
The method [self redraw]; being the key here.
Is there a better way to do it? Should I be using keyValue observers (the method observeValue:forKeyPath:ofObject:change:context:)?
Notes:
All properties (so far) are assign (mostly enum, NSUInteger, CGFloat and BOOL);
All those properties are set using bindings (method bind:toObject:withKeyPath:options:). Except when loading from the filesystem (which is not important, as I already call the drawing methods on every object after the loading is done);
The value changes are only for the current object. I do not need to be told when changes occur on other objects;
I have other properties that I don't need to watch the changes on it (because it will have no effect on my output and drawing the output is kinda time-consuming).
Thanks!
Since these properties are updated using bindings, which invoke -setValue:forKey:, you can override that method instead of writing custom setters:
+ (NSArray *) keysAffectingDrawing {
static NSArray *singleton;
if (!singleton)
singleton = [NSArray arrayWithObjects:
#"property1",
#"property2",
#"property3",
nil];
return singleton;
}
- (void) setValue:(id) value forKey:(NSString *) key {
[super setValue:value forKey:key];
if ([[CustomClass keysAffectingDrawing] containsObject:key]) [self redraw];
}
(I was first inclined recommend key-value observing but agree it's not the best solution here. I think the reason is in part that there's only one object, and in part because the design doesn't follow MVC. Usually in MVC an object that draws itself isn't the one with all the properties.)
(Added: Ahh, I see. The model is responsible for rendering the properties to a bitmap, and that's what -redraw does. That's fine MVC. To make it clearer, I recommend changing the name of the method from -redraw to something like -updateImage or -renderImage, since it doesn't actually do any drawing.)
You could use the Key-Value Observing to avoid repeating in all properties setter the method call, however i think that calling the method directly in the setter is not the wrong way to do it, and could even be faster ...

getArgument of NSInvocation of current method always returns null

I want to get the name of the arguments of the current function I am in so that I can prepare loading that object from the filesystem if it's not present on the current instance. (for instance if [foo dictTest] is not available I want to load it's prior saved plist version into exactly that ivar)
I want to find the file by providing the ivar name that I provided as an argument to the current function.
This is the function code:
-(NSDictionary*)getCachedDictionary:(NSDictionary*)dict{
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:_cmd]];
NSString * firstArgument = nil;
[invocation getArgument:&firstArgument atIndex:2];
NSLog(#"Trying to get the objects ivar %#",firstArgument);
// right now during testing return nil
return nil;
}
As soon as the code reaches the NSLog I am getting a null value from firstArgument.
Why is that? Could it be possible that I would have to wait for the complete invocation of that current method I am in or is it actually better to create a proxy function that implicitly calls my class method via an invocation that eats the ivar name provided by setArgument so that I can use that argument string like I want?
Thanks a lot in advance!
P.S.: In this particular example I do not want to use KVC to identify the ivar and return it.
You've misunderstood the NSInvocation API. +[NSInvocation invocationWithMethodSignature:] creates a new NSInvocation that is keyed to accept arguments of the types defined by the method signature. It does not return an NSInvocation that corresponds to the current method invocation. This is pretty easy to see why:
- (void)doBar:(id)bip {
NSLog(#"hi there!")
}
- (void)doFoo {
NSMethodSignature *sig = [self methodSignatureForSelector:#selector(doBar:)];
NSInvocation *i = [NSInvocation invocationWithMethodSignature:sig];
}
When you create the invocation in doFoo for the doBar: method, it's obvious to see that the arguments must be empty, because doBar: hasn't been executed, and thus there is no argument. Changing #selector(doBar:) to _cmd wouldn't magically change anything.
So the next question: is there a way to get an NSInvocation for the current method invocation? Not that I know of. NSInvocation is an extremely complicated class, and constructing one from the current method would be a nightmare.
I strongly suggest finding a different approach to do whatever it is you want to do.
Even though the question is old and answered, here is a link that provides an easy and very elegant way to create an invocation instance for any selector/method that is known at compile time:
http://www.cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html

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.

What does "-(void)" mean in this function declaration? `-(void)awakeFromNib`

How come whenever I have to use awakeFromNib protocol I have to put it in this format?
-(void)awakeFromNib
What is the need for -(void)?
The -(void) is used in the declaration of the method. Presumably, you are defining it for someone else to call, rather than calling it yourself.
The - sign indicates that the method is an instance method, as opposed to a class method. It requires an object to call it, and instance variables of the object are available to it inside its definition.
The (void) indicates the return type. This method doesn't return anything, so its result can't be assigned to anything.
think of it this way
say you have a Class you created that is called "Math"
and this class has a method called "calculate". It's type as
-(int)calculate {
2+2;
return 2+2;
}
When you alloc the class and initialize the object and perform the "calculate method on that object, it's going to do the calculation 2+2 and it will return the result, 4.
If you tried
-(void)calculate {
2+2;
}
it wouldn't do anything, it would just have that 2+2 information stored in the method but the calculation would never occur.
Because the method does not return anything, and giving a void return type is how you declare that in C and Objective-C.
(void) marks the return type - in this case, void means it's returning nothing.
If it was instead -(int)awakeFromNib, you'd be expected to return an integer.
The meaning of the return value (if any) should be explained in the documentation.

Overriding / Swizzling methods from an existing shared delegate

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.