I'm new to Cocoa/Cocoa Touch, and working through a development book. I've come across situations where the #selector() operator is used. I'm a bit lost on how and when the #selector() operator should be used. Can someone provide a short and sweet explanation and example of why it's used and what benefit it gives the developer?
By the way, here is sample code taken from Apple's iPhone development site that uses #selector()
if ([elementName isEqualToString:#"entry"])
{
parsedEarthquakesCounter++;
// An entry in the RSS feed represents an earthquake, so create an instance of it.
self.currentEarthquakeObject = [[Earthquake alloc] init];
// Add the new Earthquake object to the application's array of earthquakes.
[(id)[[UIApplication sharedApplication] delegate]
performSelectorOnMainThread:#selector(addToEarthquakeList:)
withObject:self.currentEarthquakeObject waitUntilDone:YES];
return;
}
The selector operator provides a way to refer to a method provided by an object, somewhat similar to a function pointer in C. It is useful because it allows you to decouple the process of calling methods on an object. For example one piece of code could provide a method, and another piece of code could apply that method to a given set of objects.
Examples:
Test to see if an object implements a certain method:
[object respondsToSelector:#selector(methodName)]
Store a method to later call on an object;
SEL method = #selector(methodName);
[object performSelector:method];
Call a method on a different thread (useful for GUI work).
[object performSelectorOnMainThread:#selector(methodName)]
In addition to what's been said, you can also wrap up the #selector in an NSInvocation for later use. You can set the arguments to the NSInvocation a long time after it's created, and activate it when you need the message to be fired. This gives you a lot of power.
For an introduction to the concept, Scott Stevenson has a great post entitled "Dynamic Objective-C with NSInvocation".
#selector() is used each time you need to pass the name of a method as an argument to another method, a function or as a variable value. Passing directly the name doesn't work in objective-C.
One practical example is validateMenuItem method where menu items are identified with their target actions.
Simplified example:
- (BOOL)validateMenuItem:(NSMenuItem *)item {
if ([item action] == #selector(selectFiles:) && otherCondition) {
return YES;
} else {
return NO;
}
}
One reference to look at:
http://en.wikipedia.org/wiki/Multiple_dispatch
You can use a selector to invoke a method on an object—this provides the basis for the implementation of the target-action design pattern in Cocoa.
[myObject performSelector:#selector(runMYmethod:) withObject:parameters];
is equivalent to:
[myObject runMYmethod:parameters];
Related
DISCLAIMER: This is a long post, but could prove very valuable for those grappling with using the new ObjectiveC JavascriptCore framework and doing asynchronous coding between ObjC and JS.
Hi there, I'm super new to Objective C and am integrating a javascript communication library into my iOS app.
Anyway, I've been trying my hand at using the new ObjectiveC JavaScriptCore Framework introduced in iOS7. It's pretty awesome for the most part, though quite poorly documented so far.
It's really strange mixing language conventions, but also kind of liberating in some ways.
I should add that I am of course using ARC, so that helps a lot coming from the Javascript world. But I have a question that's pretty specific around memory use issues when moving between ObjectiveC and the JSContext callBacks. Like if I execute a function in Javascript that then does some asynchronous code, and then calls back to a defined ObjectiveC block, and then that calls a defined JS callback... I just want to make sure I'm doing it right (ie. not leaking memory some place)!
Just to do things proper (because I reference a the class self to call the ObjectiveC callBacks I create a weakSelf so it plays nice with ARC (referenced from question: capturing self strongly in this block is likely to lead to a retain cycle):
__unsafe_unretained typeof(self) weakSelf = self;
Now, say I have a JSContext and add a function to it. I want this function to take a callBack function and call it with "Hello" as an argument as well as pass ANOTHER function as a callBack. ie.
// Add a new JSContext.
JSContext context = [[JSContext alloc] initWithVirtualMachine:[[JSVirtualMachine alloc] init]];
// Add a function to the context. This function takes a callBack function and calls it back with "Hello"
[context evaluateScript: #"var functionA = function(callBack){
var aMessage = "Foo";
callBack(aMessage, function(message){
/* message should say: Foo Bar */
});
}" ];
// Note, if you try to copy this code, you will have to get rid of the returns in the JS script.
Okay, so we have our basic JS side of things. Now to add the ObjectiveC complexity. I'm going to add the first ObjectiveC CallBack block:
context[#"functionB"] = ^(NSString *theMessage, JSValue *theCallBack){
[weakSelf objCFunction:theMessage withCallBack:theCallBack];
};
In the same class all this is happening in I also have the method definition. This is the place that causes the most concern to me:
-(void)objCFunction:(NSString *)message withCallBack:(JSValue *)callBack
{
NSString *concatenatedString = [NSString stringWithFormat:#"%#%#", message, #"Bar"];
[callBack callWithArguments:#[concatenatedString]];
}
So when I call:
[context evaluateScript: #"functionA(functionB);" ];
It should pass through the chain, and it does exactly what I expect it to do.
My main concern is that I hope I'm not somehow capturing a JSValue somewhere along this chain that is then leaking out.
Any help in helping me understand how ARC/the JSMachine would manage this approach to calling callBacks fluidly between Objective C and Javascript, would be super valuable!
Also, I hope this question helps others out there who are experimenting with this framework.
Thanks!
The problem with retain cycles occurs when you have two objects, each of which retains part of another. It's not specific to JavascriptCore. It's not even specific to blocks although blocks make the problem much easier to blunder into.
E.g.
#interface ObjcClass : NSObject
#property (strong,nonatomic) JSValue *badProp;
- (void) makeEvilRetainWithContext:(JSContext *) context;
#end
- (void) makeEvilRetainWithContext:(JSContext *) context{
context[#"aFunc"]=^(JSValue *jsValue){
self.badProp=jsValue;
};
}
The self.context[#"aFunc"] now retains the ObjcClass object because self.badProp is now inside the function obj inside the context created by assigning the block to #"aFunc". Likewise, the context is retained because one of its own strongly retained values is retained in self.badProp.
Really, the best way to avoid all this is just to not try and store JSValue in objective-c objects ever. There really doesn't seem to be a need to do so e.g.
#property (strong,nonatomic) NSString *goodProp;
- (void) makeGoodFunc:(JSContext *) context;
#end
- (void) makeGoodFunc:(JSContext *) context{
context[#"aFunc"]=^(JSValue *jsValue){
self.goodProp=[JSValue toString];
};
}
You code isn't a problem because simply passing a JSValue (even a function) through a method won't retain it.
Another way to think of it might be: After, objCFunction:withCallBack: executes, would there be anyway for the object represented by self to access the JSValue passed as callBack? If not, then no retain cycle.
Check out the WWDC introduction "Integrating JavaScript into Native Apps" session on Apple's developer network: https://developer.apple.com/wwdc/videos/?id=615 - it contains a section on Blocks and avoiding capturing JSValue and JSContext
In your sample code above, all the JSValues are passed as arguments (the way Apple recommends) so the references only exist whilst the code is executed (no JSValue objects are captured).
I have a method which is used widely throughout my app, it looks like so:
-(void)commandWithParams:(NSMutableDictionary*)params command:(NSString *) command method: (NSString *) method onCompletion:(JSONResponseBlock)completionBlock { }
This is used to carry out REST calls to an API. After using it all over the place i realize that i need to add another parameter (which will be used only some of the time), and i'm wondering what the best way to do this is.
I thought about using a using a parameter which defaults to nil if not specified, but apparently this is a no-go in objective c (?)
How should i go about changing it? Do i have to change it everywhere it's called and pass nil? If so, any neat functions in xCode to do this without too much hassle?
Thanks.
There are no optional arguments in Objective-C. What you could do instead is use two separate methods.
Imagine you had this method:
- (void)methodWithArgument:(NSString *)str {
// block A
}
Now you need to add a new argument, but don't want to specify it everywhere in your code.
Create a new method with the additional argument and move your implementation to it. How you handle the additional argument, depends on what the method does. From your old method, call the new one with nil as the new argument:
- (void)methodWithArgument:(NSString *)str andArgument:(NSString *)str2 {
if (str2 != nil) {
// do something.
}
// block A
}
- (void)methodWithArgument:(NSString *)str {
[self methodWithArgument:str andArgument:nil];
}
This will work as if you had a method with an optional parameter, that defaults to nil (or whatever you chose). It's a common design pattern and you see it all over Apple's frameworks too.
As an alternative to the method above you may do refactoring in AppCode. It allows to do such of things.
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 ...
I have two static methods/selectors in the same class, one passes the other as a callback to an external method. However, how I have it coded I get an error. This worked when both the methods were instance methods, and I've read it can work when the first method is an instance method using [self class]. However, I haven't found information when both are static, and I haven't got it to work.
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Thanks!
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
That line of code is setup to call the instance method Parse:, not a class method as you have it defined.
Objective-C does not have static methods. It has class methods and instance methods.
As well, your methods should start with lowercase letters.
Herp-da-derp. Dave is right.
Given this:
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Some comments:
methods should start with lowercase letters
it is exceedingly odd to use a class in such a role; even if you really only ever need one of 'em, use an instance. At the least, the instance is a convenient bucket to toss state in and it'll make refactoring in the future much easier if you ever need two.
The above pattern makes the assumption (and I ASSumed) that the instance of Callback is retained. For callbacks, timers, and some other patterns, this is typical; retain the target until the target is called for the last time. Then release (or autorelease). However, notification centers do not do this. Nor are delegates retained, typically.
Turns out the code is written correctly to do what I wanted to, but because callback was set to autorelease, the object was getting released before the callback was being processed.
I am trying to build an iPhone app. I created a
method like this:
- (void)score {
// some code
}
and I have tried to call it in an other method like this:
- (void)score2 {
#selector(score);
}
But it does not work. So, how do I call a method correctly?
To send an objective-c message in this instance you would do
[self score];
I suggest you read the Objective-C programming guide
Objective-C Programming Guide
I suggest you read The Objective-C Programming Language. The part about messaging is specifically what you want here, but the whole thing will help you get started. After that, maybe try doing a few tutorials to get a feel for it before you jump into making your own apps.
calling the method is like this
[className methodName]
however if you want to call the method in the same class you can use self
[self methodName]
all the above is because your method was not taking any parameters
however if your method takes parameters you will need to do it like this
[self methodName:Parameter]
I think what you're trying to do is:
-(void) score2 {
[self score];
}
The [object message] syntax is the normal way to call a method in objective-c. I think the #selector syntax is used when the method to be called needs to be determined at run-time, but I don't know objective-c well enough to give you more information on that.
Use this:
[self score]; you don't need #sel for calling directly
syntax is of objective c is
returnObj = [object functionName: parameters];
Where object is the object which has the method you're calling. If you're calling it from the same object, you'll use 'self'. This tutorial might help you out in learning Obj-C.
In your case it is simply
[self score];
If you want to pass a parameter then it is like that
- (void)score(int x) {
// some code
}
and I have tried to call it in an other method like this:
- (void)score2 {
[self score:x];
}
use this,
[self score];
instead of #selector(score).
[self score]; instead of #selector(score)