I'm having a problem with performSelector. It works if I call a method without parameters, but it doesn't even call the method if I pass a parameter.
Example:
- (void)test
{
NSLog(#"test"); //it works!!
}
...
[self performSelector:#selector(test) withObject:nil afterDelay:1.0];
- (void)switchOn:(NSNumber *) index
{
NSLog(#"switchOn"); //it doesn't work :-(
}
....
NSLog(#"int is %d", [((NSNumber *)obj) intValue]); //print the correct value
[self performSelector:#selector(switchOn:) withObject:obj afterDelay:1.0];
I get no errors neither. Where could it be the problem?
thanks
What is the type of the parameter for the switchOn: selector?
It must be of type id otherwise performSelector:WithObject: won't work. To quote the docs:
aSelector should identify a method that takes a single argument of type id. For methods with other argument types and return values, use NSInvocation.
performSelectorWithObject: sends a message to the selector with the object you supplied as the first argument. The receiving method must accept a single parameter of type id. For anything else use NSInvocation.
You might want to check out a similar question about this.
Try to use:
- (void)switchOn:(id)index
Related
I'm writing a simple app with 10 buttons on it. One button looks like this:
- (IBAction)num1pressed:(UIButton *)sender
{
// checkSomethingFirst
self.myLabel.text=[NSString stringWithFormat:#"1"];
}
Every button needs to "check something first" so rather than copy and paste the lines of code into every (IBAction)num#pressed code segment, I want to call a function called checkSomethingFirst. Maybe function is the wrong word here? Nevertheless my function looks like this:
-(void)checkSomethingFirst
{
// check first thing
// check other thing
// check last thing
}
When I compile my code - which looks like this:
- (IBAction)num1pressed:(UIButton *)sender
{
checkSomethingFirst;
self.myLabel.text=[NSString stringWithFormat:#"1"];
}
I get an error at checkSomethingFirst; <--- Use of undeclared identifier 'checkSomethingFirst'.
How do I create a simple function or procedure that all of my buttons can use? Note I don't need to return any values as my checkSomethingFirst function simply sets a label to either a 0 or a 1 (probably better off using a bool, except I'm doing the setting on a global label - hence no need to return a bool, I can simply interrogate the value in the label).
You need to say what object the method should be called on
[self checkSomethingFirst];
You are calling your method in the wrong way
- (IBAction)num1pressed:(UIButton *)sender
{
[self checkSomethingFirst];
self.myLabel.text=[NSString stringWithFormat:#"1"];
}
You need to call that method using
[self checkSomethingFirst];
hope it helps.
- (IBAction)num1pressed:(UIButton *)sender
{
// checkSomethingFirst
[self checkSomethingFirst];
self.myLabel.text=[NSString stringWithFormat:#"1"];
}
I am a beginner of iOS development and while going through this document (iOS Developer Guide about configuring a TableView with Indexed List) I came across this:
// Listing 4.7
for (State *theState in statesTemp) {
NSInteger sect = [theCollation sectionForObject:theState collationStringSelector:#selector(name)];
theState.sectionNumber = sect;
}
I could not figure out the selector (#selector(name)) and its purpose, nor could I find the method with the name passed in the selector i.e. name. I googled for examples to find a better explanation, and came across this example.
In the code listing, there is a statement which is a method call:
self.tableData = [self partitionObjects:objects collationStringSelector:#selector(title)];
now the selector is called title. I have not been able to find a better explanation, and my question is what is the purpose of this selector and the method referred by this selector, and what should it do and return.
In general
With the #selector(title:) you define which method will be called.
in my example it will call
- (void) title:(id)someObject {}
Be carefull with the semicolon at the end! If you have a semicolon at the end you method will have parameters like mine above.
Your code states just #selector(title) and will call a method title without a parameter like this:
- (void)title {}
Specific to UILocalizedIndexCollation
The docs state:
selector
A selector that identifies a method returning an identifying
string for object that is used in collation. The method should take no
arguments and return an NSString object. For example, this could be a
name property on the object.
So i would suggest you implement it like this
self.tableData = [self partitionObjects:objects collationStringSelector:#selector(title)];
...
- (NSString *)title {
NSString *title;
// some code to fill title with an identifier for your object
return title;
}
Try replace the title with self:
self.tableData = [self partitionObjects:objects collationStringSelector:#selector(self)];
worked for me
I have a singleton class that is instantiated as follows:
#import "FavoritesManager.h"
static FavoritesManager *sharedFavoritesManager = nil;
#implementation FavoritesManager
+ (id)sharedManager {
#synchronized(self) {
if (sharedFavoritesManager == nil) {
sharedFavoritesManager = [[self alloc] init];
}
}
return self;
}
This returns an object, but for some reason it will only respond to class methods. If I call a instance method I get
+[FavoritesManager testMethod]: unrecognized selector sent to class 0x59198
For what it's worth, this is what testMethod looks like:
- (void)testMethod {
NSLog(#"Test");
}
and I'm absolutely positive it's declared in the interface. I've used this exact code in other classes and it works like a charm, so I don't really understand what the problem is here. One thing that is suspicious is the plus sign in +[FavoritesManager testMethod], but I can't explain it. Any ideas?
EDIT: I was confusing public/private and class/method terminology. Thanks to everyone who pointed that out.
If you want to call testMethod from another class method then you need:
+ (void)testMethod {
NSLog(#"Test");
}
The reason is that if you call a class method then there's no instance, so nothing on which to call instance methods. But probably you want to call:
[[FavoritesManager sharedManager] testMethod];
Which means 'get the shared instance, then call testMethod on it'. Thinking as I type, you might also like to add:
+ (void)forwardInvocation:(NSInvocation *)anInvocation
{
id sharedManager = [self sharedManager];
if ([sharedManager respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:sharedManager];
else
[super forwardInvocation:anInvocation];
}
Which is the Objective-C means for message forwarding. So if the metaclass FavoritesManager receives a message it can't respond to, it lets its shared manager instance have a go. That means that:
[FavoritesManager testMethod];
Becomes functionally equivalent to (though a little slower than):
[[FavoritesManager sharedManager] testMethod];
Providing that you haven't implemented a class method in addition to an instance method. You can learn more about message forwarding in Apple's official documentation.
The error indicates that you're sending the message testMethod to the class, rather than an instance.
The reason for this is that your sharedManager method is incorrect. You are currently returning self, which, in this class method, is the class itself. This means that when you write [[FavoritesManager sharedManger] testMethod] you end up sending testMethod to the class. Since testMethod isn't a class method, you get an error.
You should have return sharedFavoritesManager; at the end of sharedManager, not return self;. The latter is correct only in instance method initializers.
Also, as dbrajkovic commented, you seem to be confused about public/private and class/instance methods. Strictly, ObjC has no private methods. You can hide the declaration, which will cause a compiler warning, but the message will still be sent and the method will be called. The + and - distinguish class methods from instance methods; the distinction is which kind of object you send a message to. Info here: What is the difference between class and instance methods?
The error is right you must be calling [FavoritesManager testMethod] which means you're trying to call a class method. I believe you want [[FavoritesManager sharedManager] testMethod];
+ at the start of a method declaration means that it's a class method, - means that it's an instance method. Do this:
+(void)testMethod {
NSLog(#"Test");
}
If you want to invoke testMethod on your sharedManager, then keep the testMethod declaration as you have it and instead change your invocation to
[[FavoritesManager sharedFavoritesManager] testMethod];
Either will work, and choosing between the two is a matter of app design.
Instead try
[[FavoritesManager sharedFavoritesManager] testMethod];
there are no priavte methods in obj-c.
But anyway on a singleton you are always calling from the outside of the class, so only declare "public methods". for detailed help post your code.
Call your singleton instance:
[[ FavoritesManager sharedManager] testMethod];
I am getting an error "void value not ignored as it ought to be" on the line:
home = [[Home alloc] initWithPhoto:imageView.image];
Please help.
Your Home class' -initWithPhoto: function is probably returning void. Initializer functions are supposed to return id.
What is the return value of initWithPhoto? The error means that the method is returning void but you are assigning that to something. initWithPhoto should look something like this:
- (id)initWithPhoto:(UIImage *)image {
if (self = [super init]) {
// do your tasks
}
return self;
}
The method should return id, not void.
Your initWithPhoto method is returning an object of type void when it should be returning an object of type id, especially if you're expecting the home variable to contain anything useful. Try editing the initWithPhoto method to return an object of type id.
Is there a way to verify that a method has been called 'x' amount of times?
Looking at the test file for OCMock, it seems that you need to have the same number of expects as you have calls. So if you call someMethod three times, you need to do...
[[mock expect] someMethod];
[[mock expect] someMethod];
[[mock expect] someMethod];
...test code...
[mock verify];
This seems ugly though, maybe you can put them in a loop?
I've had success by leveraging the ability to delegate to a block:
OCMStub([mock someMethod]).andDo(^(NSInvocation *invocation)
{ /* block that handles the method invocation */ });
Inside the block, I just increment a callCount variable, and then assert that it matches the expected number of calls. For example:
- (void)testDoingSomething_shouldCallSomeMethodTwice {
id mock = OCMClassMock([MyClass class]);
__block int callCount = 0;
OCMStub([mock someMethod]).andDo(^(NSInvocation *invocation) {
++callCount;
});
// ...exercise code...
int expectedNumberOfCalls = 2;
XCTAssertEqual(callCount, expectedNumberOfCalls);
}
The block should be invoked each time someMethod is called, so callCount should always be the same as the number of times the method was actually called.
If you need to check if a method is only called once, you can do it like this
[self.subject doSomething];
OCMVerify([self.mock method]);
OCMReject([self.mock method]);
[self.subject doSomething];