Warning about making pointer from integer without a cast -- explanation needed - objective-c

I have this code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic
NSLog(#"didSelectRowAtIndexPath");
//The hud will dispable all input on the view
HUD = [[MBProgressHUD alloc] initWithView:self.view];
// Add HUD to screen
[self.view addSubview:HUD];
// Regisete for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
HUD.labelText = #"Loading Events, Please Wait..";
int i = indexPath.row;
//Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(loadData:) onTarget:self withObject:i animated:YES];
}
And I get this warning:
warning: passing argument 3 of 'showWhileExecuting:onTarget:withObject:animated:' makes pointer from integer without a cast
Can somebody please explain what I'm doing wrong here? Could someone also briefly explain the situation with ints in Objective-C, coming from Java I find it odd that they are so confusing to use.

The problem is that showWhileExecuting:onTarget:withObject:animated: takes an object as its third argument. To get aroung this, you can wrap integers as objects using the NSNumber class
[NSNumber numberWithInt:i]
You will then have to unwrap the argument in the loadData: method by calling
[argument intValue]

The method takes an object as a third argument (withObject), but you passed an int instead.

Apparently, you provided an integer(int i) instead of an object pointer(type of id). It is not safe. Use NSNumber instead.
int i;
...
NSNumber * numberI = [NSNumber numberWithInt:i];
[HUD showWhileExecuting:#selector(loadData:) onTarget:self withObject:i animated:YES];

All of the answers above are the "correct" ones. I.e. be a good boy and use and NSNumber to pass the value.
However, … the following will work
"damn you, compiler, i'm smarter than you are:"
(cast your integer, totally not a valid object, to id)
[HUD showWhileExecuting:#selector(loadData:)
onTarget:self
withObject:(id)i
animated:YES];
i'm guessing (you didn't say), that your load data method looked like this:
- (void)loadData:(int)i { …
you will see code like this, which is the only reason i mentioned it.
you should be familiar with it.
someone thinks that saving 1 object allocation is going to make their code efficient; don't sweat object allocations, and wrap it up in an NSNumber as shown above
most C compilers will handle this correctly, but it's not guaranteed

Related

Can't add to an NSMutableArray with addObject method

I am having an issue with the addObject method of an NSMutableArrayObject. Here's the code I'm using right now:
- (void)addBirdSightingWithName:(NSString *)name location:(NSString *)location {
BirdSighting *bird;
NSDate *today = [NSDate date];
bird = [[BirdSighting alloc] initWithName:name location:location date:today];
[self.masterBirdSightingList addObject:bird];
NSLog(#"Elements: %d", [self.masterBirdSightingList count]);
}
When this code runs, the NSLog call prints the value 0 to the console. I don't know what could be causing this.
EDIT:
I have looked deeper into the code, and I have discovered that the problem is that my BirdSightingDataController is never initialized. Now my question is: Where can I place the init for my BirdSightingDataController? In the viewDidLoad?
Thanks to everyone for the help.
Did you allocate memory to masterBirdSightingList?
self.masterBirdSightingList = [[NSMutableArray alloc] init];
In almost every case where cellForRowAtIndexPath: is not called is because numberOfRowsInSection: returns 0.
Place a log there and make sure you return more than one item and you should be able to see your cells. If you need further help please post the code in your:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
Check your property accessor. Are you sure it's returning the correct object? Make sure the name of the property matches the instance variable or you've specified it correctly (For example: #synthesize masterBirdSightingList = _masterBirdSightingList;. If the property accessor doesn't match the iVar, it will return nil. Of course, if you're manually implementing the accessor check your code there. If you're not, you could also try manually implementing it to make sure.
To do a quick check, remove the self.masterBirdSightingList and replace it with masterBirdSightingList (assuming that's the iVar name) to access the iVar directly and see what happens.

String Problems

A very stupid question from a noob.
I have an action, that sets the string of a label.
- (IBAction) changeProductText:(NSString *)str{
lblProductTxt.text = str;
}
This is the string I want to set that to:
TestText = [NSString stringWithFormat:#"Hi"];
And this is how I am doing it:
[self.navigationController pushViewController:nextController animated:YES];
[nextController changeProductText:TestText];
My problem is that it wont set the string to anything is random whats going into the string. It may crash when I click on the cell it may not, so I am doing something wrong.
stringWithFormat gives you an autoreleased format, without seeing more of the code I'm guessing its hitting an autorelease pool and you're trying to access garbage that was your string.
Is this the exact sequence of the statements?
[self.navigationController pushViewController:nextController animated:YES];
[nextController changeProductText:TestText];
I am not 100% sure but I believe that the second line will not be executed before the nextController is being pushed.
Try to reverse them.
(1st create and initialize the nextController)
2nd assign all values that you want to pass down to nextController
3rd push nextViewController on the stack of View Controllers.
[nextController changeProductText:TestText];
[self.navigationController pushViewController:nextController animated:YES];
The only parameter of an IBAction is the sender:
- (IBAction) clickMyButton: (id) sender;
A string is hardly a valid sender for an action, so whatever you are setting to lblProductTxt.text, it is not a string, it is the sender that performs the action.
In your action method, you can of course set lblProductTxt.text. You'll have to find out yourself where you get the string.
Update
From your comments I deduce you don't have an IBAction, you simply have a void method. Your use of IBAction got me on the wrong foot. Declare it like:
- (void) changeProductText: (NSString *) newText;
Omit the (IBAction) designator, as that is only necessary for, well, real IB action methods.
No matter if you use
NSString *testText = [NSString stringWithFormat: #"Hi"];
or
NSString *testText = [NSString stringWithString: #"Hi"];
The result is exactly the same: an autoreleased NSString with the text "Hi". Only the way it was created is slightly different. If one works and the other crashes, then the same thing is wrong and you are just lucky it doesn't crash.
Now what is wrong is impossible to see from what you posted so far.

iPhone UISlider action method

In the current book I am reading, the author implements an IBAction for a slider in the following way (see below V001). To my eye, it seemed a little over complicated so I re-factored the code (V002). Am I right in thinking that sender is a pointer to the object that fired the event? Also, is there any downside to casting sender in the header, rather than leaving it as sender and casting it in the method body?
v001
-(IBAction)sliderChange:(id)sender {
UISlider *slider = (UISlider *)sender;
int progressAsInt = (int)([slider value] + 0.5f);
NSString *newText = [[NSString alloc] initWithFormat:#"%d", progressAsInt];
[sliderLabel setText:newText];
[newText release];
}
v002
-(IBAction)sliderChange:(UISlider*)sender {
NSString *newText = [[NSString alloc] initWithFormat:#"%d",(int)[sender value]];
[sliderLabel setText:newText];
[newText release];
}
gary
id a special type that can hold any object. The nuance is that you don't have well-defined type safety. You can call any selector on any object, and if it exists it will be invoked.
In V001, doing [slider value] instead of [sender value] makes more logical sense which I think is probably why you took to refactoring in the first place because it didn't appear to be called.
In V002 [sender value] retrieves the float property of the same UISlider but hides the fact that you may not be getting a a slider object, and it could be on any object.
This is a matter of style and discrimination. I am pretty diligent in my own code to determine which concrete object I am trying to access a selector, and will even go so far as calling isKindOfClass, and verifying I am calling the selector on the correct UISlider object. To answer your downside question: the type of the object isn't obviated as it should be when using id.
Why? I want multiple sliders on the same view handling the slider event, I don't want one slider to impact the data of both, even if I may want to handle them in the same way.

How to use native C types with performSelectorOnMainThread:?

I'd like to call (void)setDoubleValue:(double)value using performSelectorOnMainThread:.
What I thought would work is:
NSNumber *progress = [NSNumber numberWithDouble:50.0];
[progressIndicator performSelectorOnMainThread:#selector(setDoubleValue:)
withObject:progress
waitUntilDone:NO];
Didn't work.
Then I implemented a helper method:
- (void)updateProgressIndicator:(NSNumber *)progress
{
[progressIndicator setDoubleValue:[progress doubleValue]];
}
Works, but not really clean.
After that I tried it with NSInvocation.
NSInvocation *setDoubleInvocation;;
SEL selector = #selector(setDoubleValue:);
NSMethodSignature *signature;
signature = [progressIndicator methodSignatureForSelector:selector];
setDoubleInvocation = [NSInvocation invocationWithMethodSignature:signature];
[setDoubleInvocation setSelector:selector];
[setDoubleInvocation setTarget:progressIndicator];
double progress = 50.0;
[setDoubleInvocation setArgument:&progress atIndex:2];
[setDoubleInvocation performSelectorOnMainThread:#selector(invoke)
withObject:nil
waitUntilDone:NO];
This solution works, but it uses a lot of code and is quite slow. (Even if I store the invocation.)
Is there any other way?
If you are on Snow Leopard, you can use Blocks:
dispatch_async(dispatch_get_main_queue(), ^{
[progressIndicator setDoubleValue: 50.0];
});
you'll need to write a custom un-boxing method to wrap setDoubleValue:.
- (void) setDoubleValueAsNumber: (NSNumber *) number {
[self setDoubleValue: [number doubleValue]];
}
Dave Dribin has a solution for this that takes the shape of a category on NSObject. His category wraps the method call in an NSInvocation and invokes that on the main thread. This way, you can use whatever method interface you like, including primitive types for your arguments.
The Amber framework also has a category on NSObject that adds a main thread proxy, where any messages sent to that proxy are executed on the main thread.
This blog post: http://www.cimgf.com/2008/03/01/does-objective-c-perform-autoboxing-on-primitives/ points out that while Cocoa won't autobox primitives, it will unbox them automatically. So numbers and BOOLs at least can be passed in as an NSNumber class, and the called function will automatically unbox it. I've been playing with using a proxy object (Uli's UKMainThreadProxy*) which works quite well, although I'm sure it has it's limitations like anything else.
http://zathras.de/programming/cocoa/UKKQueue.zip/UKKQueue/UKMainThreadProxy.m

How to use performSelector:withObject:afterDelay: with primitive types in Cocoa?

The NSObject method performSelector:withObject:afterDelay: allows me to invoke a method on the object with an object argument after a certain time. It cannot be used for methods with a non-object argument (e.g. ints, floats, structs, non-object pointers, etc.).
What is the simplest way to achieve the same thing with a method with a non-object argument? I know that for regular performSelector:withObject:, the solution is to use NSInvocation (which by the way is really complicated). But I don't know how to handle the "delay" part.
Thanks,
Here is what I used to call something I couldn't change using NSInvocation:
SEL theSelector = NSSelectorFromString(#"setOrientation:animated:");
NSInvocation *anInvocation = [NSInvocation
invocationWithMethodSignature:
[MPMoviePlayerController instanceMethodSignatureForSelector:theSelector]];
[anInvocation setSelector:theSelector];
[anInvocation setTarget:theMovie];
UIInterfaceOrientation val = UIInterfaceOrientationPortrait;
BOOL anim = NO;
[anInvocation setArgument:&val atIndex:2];
[anInvocation setArgument:&anim atIndex:3];
[anInvocation performSelector:#selector(invoke) withObject:nil afterDelay:1];
Just wrap the float, boolean, int or similar in an NSNumber.
For structs, I don't know of a handy solution, but you could make a separate ObjC class that owns such a struct.
DO NOT USE THIS ANSWER. I HAVE ONLY LEFT IT FOR HISTORICAL PURPOSES. SEE THE COMMENTS BELOW.
There is a simple trick if it is a BOOL parameter.
Pass nil for NO and self for YES. nil is cast to the BOOL value of NO. self is cast to the BOOL value of YES.
This approach breaks down if it is anything other than a BOOL parameter.
Assuming self is a UIView.
//nil will be cast to NO when the selector is performed
[self performSelector:#selector(setHidden:) withObject:nil afterDelay:5.0];
//self will be cast to YES when the selector is performed
[self performSelector:#selector(setHidden:) withObject:self afterDelay:10.0];
Perhaps NSValue, just make sure your pointers are still valid after the delay (ie. no objects allocated on stack).
I know this is an old question but if you are building iOS SDK 4+ then you can use blocks to do this with very little effort and make it more readable:
double delayInSeconds = 2.0;
int primitiveValue = 500;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self doSomethingWithPrimitive:primitiveValue];
});
PerformSelector:WithObject always takes an object, so in order to pass arguments like int/double/float etc..... You can use something like this.
//NSNumber is an object..
[self performSelector:#selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
Same way you can use [NSNumber numberWithInt:] etc.... and in the receiving method you can convert the number into your format as [number int] or [number double].
Blocks are the way to go. You can have complex parameters, type safety, and it's a lot simpler and safer than most of the old answers here. For example, you could just write:
[MONBlock performBlock:^{[obj setFrame:SOMETHING];} afterDelay:2];
Blocks allow you to capture arbitrary parameter lists, reference objects and variables.
Backing Implementation (basic):
#interface MONBlock : NSObject
+ (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay;
#end
#implementation MONBlock
+ (void)imp_performBlock:(void(^)())pBlock
{
pBlock();
}
+ (void)performBlock:(void(^)())pBlock afterDelay:(NSTimeInterval)pDelay
{
[self performSelector:#selector(imp_performBlock:)
withObject:[pBlock copy]
afterDelay:pDelay];
}
#end
Example:
int main(int argc, const char * argv[])
{
#autoreleasepool {
__block bool didPrint = false;
int pi = 3; // close enough =p
[MONBlock performBlock:^{NSLog(#"Hello, World! pi is %i", pi); didPrint = true;} afterDelay:2];
while (!didPrint) {
[NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeInterval:0.1 sinceDate:NSDate.date]];
}
NSLog(#"(Bye, World!)");
}
return 0;
}
Also see Michael's answer (+1) for another example.
I would always recomend that you use NSMutableArray as the object to pass on. This is because you can then pass several objects, like the button pressed and other values. NSNumber, NSInteger and NSString are just containers of some value. Make sure that when you get the object from the array
that you refer to to a correct container type. You need to pass on NS containers. There you may test the value. Remember that containers use isEqual when values are compared.
#define DELAY_TIME 5
-(void)changePlayerGameOnes:(UIButton*)sender{
NSNumber *nextPlayer = [NSNumber numberWithInt:[gdata.currentPlayer intValue]+1 ];
NSMutableArray *array = [[NSMutableArray alloc]initWithObjects:sender, nil];
[array addObject:nextPlayer];
[self performSelector:#selector(next:) withObject:array afterDelay:DELAY_TIME];
}
-(void)next:(NSMutableArray*)nextPlayer{
if(gdata != nil){ //if game choose next player
[self nextPlayer:[nextPlayer objectAtIndex:1] button:[nextPlayer objectAtIndex:0]];
}
}
I also wanted to do this, but with a method that receives a BOOL parameter. Wrapping the bool value with NSNumber, FAILED TO PASS THE VALUE. I have no idea why.
So I ended up doing a simple hack. I put the required parameter in another dummy function and call that function using the performSelector, where withObject = nil;
[self performSelector:#selector(dummyCaller:) withObject:nil afterDelay:5.0];
-(void)dummyCaller {
[self myFunction:YES];
}
I find that the quickest (but somewhat dirty) way to do this is by invoking objc_msgSend directly. However, it's dangerous to invoke it directly because you need to read the documentation and make sure that you're using the correct variant for the type of return value and because objc_msgSend is defined as vararg for compiler convenience but is actually implemented as fast assembly glue. Here's some code used to call a delegate method -[delegate integerDidChange:] that takes a single integer argument.
#import <objc/message.h>
SEL theSelector = #selector(integerDidChange:);
if ([self.delegate respondsToSelector:theSelector])
{
typedef void (*IntegerDidChangeFuncPtrType)(id, SEL, NSInteger);
IntegerDidChangeFuncPtrType MyFunction = (IntegerDidChangeFuncPtrType)objc_msgSend;
MyFunction(self.delegate, theSelector, theIntegerThatChanged);
}
This first saves the selector since we're going to refer to it multiple times and it would be easy to create a typo. It then verifies that the delegate actually responds to the selector - it might be an optional protocol. It then creates a function pointer type that specifies the actual signature of the selector. Keep in mind that all Objective-C messages have two hidden first arguments, the object being messaged and the selector being sent. Then we create a function pointer of the appropriate type and set it to point to the underlying objc_msgSend function. Keep in mind that if the return value is a float or struct, you need to use a different variant of objc_msgSend. Finally, send the message using the same machinery that Objective-C uses under the sheets.
You Could just use NSTimer to call a selector:
[NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(yourMethod:) userInfo:nil repeats:NO]
Calling performSelector with an NSNumber or other NSValue will not work. Instead of using the value of the NSValue/NSNumber, it will effectively cast the pointer to an int, float, or whatever and use that.
But the solution is simple and obvious. Create the NSInvocation and call
[invocation performSelector:#selector(invoke) withObject:nil afterDelay:delay]
Pehaps...ok, very likely, I'm missing something, but why not just create an object type, say NSNumber, as a container to your non-object type variable, such as CGFloat?
CGFloat myFloat = 2.0;
NSNumber *myNumber = [NSNumber numberWithFloat:myFloat];
[self performSelector:#selector(MyCalculatorMethod:) withObject:myNumber afterDelay:5.0];