I currently use objc_msgSend to invoke such selector on collection of object. Is there any better way to do that? Here is my code:
#protocol ADelegateProtocol {
-(void) timeToEventOneDidChange:(NSInterval) event1;
-(void) timeToEventTwoDidChange:(NSInterval) event1;
}
- (void) delegatesPerformSelector:(SEL) selector withTimeIntervalAsFristParameter:(NSTimeinterval) timeInterval {
for (id<ADelegateProtocol> delegate in delegates) {
if([delegate respondsToSelector:selector]) {
objc_msgSend(delegate, selector, timeInterval);
}
}
}
The selector is passed in as a parameter, timeInterval is a non-object value.
Note: I don't want to use KVO.
If you are going to use objc_msgSend() you must create a correctly typecast function pointer to do so. Relying on varargs to map to non-varargs doesn't work in all cases.
I.e. You'd want:
void (*myMessage)(id, SEL, NSTimeInterval) = objc_msgSend;
myMessage(delegate, aSelector, aTimeInterval);
(typed into SO -- consider the syntax an approximation. :)
What you can use beside objc_msgSend (which of course works), is NSInvocation. Personally I prefer the objc_msgSend way as its the most overhead free way to do this. Its also the more faster way, but this shouldn't matter in a normal App (it does matter in games).
Well, the choice is yours, both ways work and there is nothing bad with objc_msgSend or NSInvocation (beside that C code looks wrong in an ObjC method).
If delegates is an NSArray: what about using NSArray's makeObjectsPerformSelector:(SEL)aSelector.
If you need to pass an object along as a parameter, you can use makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject.
Related
can someone please tell me whether this is the correct way of doing type casting in objective c.
I have an object "myObject" which could possibly be an instance of "DataViewController".
If so i should call the "updateView" method.
if (myObject respondsToSelector:#selector(updateView:)])
{
[(DataViewController *)myObject updateView:data];
}
You do not need a cast in this case: since you've started down the selector path, you might as well call performSelector:withObject: for consistency:
if (myObject respondsToSelector:#selector(updateView:)]) {
[myObject performSelector:#selector(updateView:) withObject:data];
}
This is a more general approach, because it lets you run updateView on objects of other types, not necessarily of DataViewController, as long as they have the appropriate method.
If you would like to use a type-based solution, you could use isKindOfClass: method, like this:
if (myObject isKindOfClass:[DataViewController class]]) {
DataViewController *ctrl = (DataViewController)myObject;
[ctrl updateView:data];
}
In Java you can put in multiple constructors to a class that are called depending on the types and/or number of parameters that are used when an instance is constructed.
I assume that there is the equivalent in Objective C.
Can I have a polymorphic method?
I would like to build a method that acts slightly differently according to whether a string is passed or a double?
Does that sound bonkers or is it easy?
You're thinking of overloaded methods. Due to the way dynamic dispatch is implemented in Objective-C, it isn't currently possible to pass two unrelated types as arguments to the same (or same-named) method and have it understand.
In Objective-C, there are two related but distinct approaches to handling multiple kinds of input. Let's use your example of a string or a double as possible inputs. In Java, you might have:
void applyWidget(String s);
void applyWidget(double d);
And that's great, but not Objective-C. In Objective-C, you instead would use two different method names:
- (void)applyWidgetWithName: (NSString *)name;
- (void)applyWidgetWithValue: (double)value;
The same logic is in each method as in the Java version, but the distinct names let the compiler treat them as distinct methods (which they are, even in Java.) The code also becomes self-documenting: by reading it, you get an idea of what's happening even without comments. Alternatively, if you simply must have one method name, you change the parameter type to id and accept any object:
- (void)applyWidget: (id)widget;
Then pass either an NSString or an NSNumber wrapping your double. Then, in the implementation of the method, use Objective-C's introspection methods to determine how to proceed:
if ([widget isKindOfClass: [NSString class]]) {
...
} else if ([widget isKindOfClass: [NSNumber class]]) {
double d = [widget doubleValue];
...
}
This approach essentially tells callers "send anything--I'll handle it appropriately." It can be difficult to determine the behaviour of such a method without extensive documentation.
Absolutely easy:
- (id)initWithSomeObject:(id)object
{
if ([object isKindOfClass:[ClassOne class]) {
// do something
} else if ([object isKindOfClass:[ClassTwo class]) {
// do something else
} // etc.
return self;
}
yes, but objc does not have proper overloading.
so you see things like initWithDouble:, initWithBool: and so on. that's part of the reason it's a bit 'wordy' for some people's taste.
to use your example:
#interface MONClass
- (id)initWithString:(NSString *)pString;
- (id)initWithDouble:(double)pDouble;
...
but the following is an error:
- (id)initWith:(NSString *)pString;
- (id)initWith:(double)pDouble;
because the selector is the same -- the parameter/return types are omitted from the selector.
Basically Objective C does't have proper method overloading. It will support overriding only.
Suppose if you write functions like in same class,
(void) showMethod;
(void) showMethod:(int) aNumber;
This will support in Objective C.
Suppose if you write functions like,
(void) showMethod:(NSString*) aString;
(void) showMethod:(int) aNumber;
In this way the compiler gives Error because there conflicting parameter types in implementation of showMethod.
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.
I have a pointer in an objective-C class that I need to send messages to. The pointer can potentially be anything, so I need to make sure that it will respond to my messages before I send them. Here's the function I'm using to do the checking:
int delegatePreparedForSelector(id delegate, SEL aSelector) {
if (delegate
&& [delegate isKindOfClass:[NSObject class]]
&& [delegate respondsToSelector:aSelector]) {
return YES;
}
return NO;
}
The problem is that sometimes the delegate pointer is a struct objc-object * and I get a EXC_BAD_ACCESS bad access error when i send the isKindOfClass message.
Is there a better test i should be using to determine if the delegate will respond to my messages?
Wait, do you really mean the pointer can be anything? Like a void * pointing to a chunk of raw malloc'ed memory, or an objc_object that does not derive from NSObject? If that is really the case then there is no way to make this work safely. It is equivalent to saying "Without dereferencing this pointer how can I know it is safe to dereference?" The only way is to have a priori knowledge that whatever passed it into you did not hand you a bad pointer.
You can try to write some signal handler code to cleanup an EXEC_BAD_ACCESS, but ultimately it will work slowly, poorly, and mask lots of other real bugs. Realistically you either have some constraints on what you are being passed in, or you need to re-architect this part of your project.
It sounds like your delegate is being disposed of before the call, not that there is anything necessarily wrong with this code.
Also, you can enforce a protocol implementation on the parameter like so: id<MyDelegateProtocol> delegate instead of just using a bare id.
the delegate pointer pointing to type struct objc_object causing a problem is confusing, as all objects in Obj-C are of type (digging into an obj-c object):
struct objc_object
{
struct objc_class *isa;
/* extra stuff */
};
the *isa points to a class.. some class. So the object you're setting as a delegate might just not exist or point to bad memory.
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.