Calling Obj-C Methods - objective-c

I want to call a method B using a different method A. The problem is that the aksed parameters of method B is not present in Method A.
 
here's what I've tried ..
-(void) methodA {
// some code
CGSize *size = [CGSizeMake(self.frame.size.width, self.frame.size.height)];
[self methodB:size];
}
-(void) methodB:(CGSize) size {
//some code
}
There is certainly a better way...
Thanks

I don't think your code will be compiled.
CGSize is not an object.
Refactor to:
CGSize size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self methodB:size];

You should only wrap [] around methods. CGSizeMake(self.frame.size.width, self.frame.size.height) is a function, not a method, so get rid of the square braces. You can tell it's a function because functions look like functionName(argument1, argument2, ...) whereas methods look like [object methodName:argument1 methondNameContinued:argument2].
Further, CGSizeMake returns a CGSize structure, not a pointer to a CGSize structure, so ditch the * in *size.
Then you'll be left with this:
CGSize size = CGSizeMake(self.frame.size.width, self.frame.size.height);
[self methodB:size];
Which is correct.

Related

How to wrap a 'CGPoint' as an object argument passing to performSelectorInBackground?

I am pretty new for COCOS2D and I am now trying to pass a CGPoint as an argument to a thread function. I don't know how to have it wrapped into an object. Can anyone help? thanks in advance~
You can create an NSValue with the the CGPoint and pass that as the withObject parameter:
NSValue *value = [NSValue valueWithCGPoint:CGPointMake(1.2, 22.6)];
[self performSelectorInBackground:#selector(someSelector:) withObject:value];
And then inside your selector, you can access the CGPoint in the NSValue like so:
CGPoint point = [value CGPointValue];
Lets say, you have have
CGPoint p =ccp(10,10);
You could wrap it into an object using pointers and malloc, something like the following :-
CGPoint *wrappedP = malloc(sizeof(CGPoint));
wrappedP->x = p.x;
wrappedP->y = p.y;
Make sure to call
free(wrappedP);
when you are done with wrappedP so that the memory allocated for wrappedP can be released.
try this:
class.h:
-(void) method:(CGPoint )point;
class.m:
-(void) method:(CGPoint )point
{
NSLog(#"X:%f Y:%f",point.x,point.y);
}
// method call
[self method:CGPointMake(100.0f, 100.0f)];

Nil object reference in objective C

I am calling a class method from an outside class in order to obtain an object reference, in this case a reference to a UIImage. I can successfully call it and get it back from within myOtherClass itself, but when I call the method from myClass, it always returns nil.
#implementation myClass
...
- (UIImage *) gethThumb: (UIImage *) originalImage {
// always comes back nil:
UIImage* thumb = [MyOtherClass makeThumb: originalImage];
return thumb;
}
...
#end
#implementation MyOtherClass
...
+ (UIImage*) makeThumb: (UIImage *) fullImage {
CGSize imageSize = [fullImage size];
int shortestEdge = MIN(imageSize.width, imageSize.height);
CGRect rect = CGRectMake((imageSize.width - shortestEdge)/2, (imageSize.height - shortestEdge)/2, shortestEdge, shortestEdge);
CGImageRef imageRef = CGImageCreateWithImageInRect([fullImage CGImage], rect);
UIImage *thumb = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGSize thumbsize = CGSizeMake(180, 180);
UIGraphicsBeginImageContext(thumbsize);
[thumb drawInRect:CGRectMake(0, 0, thumbsize.width, thumbsize.height)];
UIImage *scaledThumb = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//scaledThumb is not nil here:
return scaledThumb;
}
...
#end
I stepped through and watched the value, I could see it was pointing to a valid memory address while still inside the makeThumb method, but once it had returned the pointer to the external class, the memory address was suddenly 0.
I tried writing it as an instance method and instantiating an object and then calling the method using
thumb = [self.myInstanceOfOtherClass makeThumb: originalImage];
and also changing the method to accept an object reference:
UIImage *thumbRef = nil;
thumb = [self.myInstanceOfOtherClass makeThumb: originalImage thumbRef:thumbRef];
-- in the myOtherClasss:
- (UIImage*) makeThumb: (UIImage *) fullImage thumbRef:(UIImage *) thumbRef {
...
thumbRef = scaledThumb;
...
}
... which I thought would prevent ARC from losing the reference to the object (if that's what's happening here).
I am relatively new to Objective C, and I'm hoping someone can explain to me why this is happening. Is the object being deallocated before it returns to the external class? Do I have to turn it into a property that is strong in the external class so the reference doesn't get discarded?
Forgot to mention: the reference is pointing to an image being created from a reference to a camera image in the UIImagePickerControllerDelegate method, didFinishPickingMediaWithInfo. The Camera UIView is dismissed before this thumb is created. I think maybe this is why I am losing the reference.
Thanks in advance for your help!!
You never allocated the memory for image. DO the following:
UIImage *myimage =[ [ UIImage alloc] init].

How to name a block of code and call it in a different method?

I use Grand Central Dispatch methods to do some executions of my app in a queue. I decide the frames for buttons in a calculation on that queue. I want my app to re-draw its scren and calculate new frames after rotation. Here is some pseudo code explanation from what i do:
CGFloat a=123, b=24;
dispatch_async(drawingQue, ^{
//needed loops to get the total button count-how many ones will be drawn et..
for(int x=0;x<someCount<x++){
for(int y=0;y<anotherCount;y++){
//needed frame&name ect assingments
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];}}
};
Here what i want is, how can i give this block a name and re-use it in the
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
}
delegate method? For instance, if the rotation is landscape, i want to use a=234 instead of 123.. Any help please. Thanks in advance..
Declare an instance variable of block type and use Block_copy to keep the block:
#interface My {
void (^myBlock)(void);
}
#end
myBlock = Block_copy(^{
...block code...
});
// later call it
myBlock();
// don't forget to release it in dealloc
It is important to copy the block before storing it outside of the scope of its literal (^{...}), because the original block is stored on stack and will die when the scope exits.
Just make a #property that's a block, store it, and use it again later:
typedef void (^MyBlock)(CGFloat, CGFloat);
...
#property(readwrite, copy) MyBlock buttonFramesBlock;
...
#synthesize buttonFramesBlock;
...
self.buttonFramesBlock = ^(CGFloat a, CGFloat b){
//needed loops to get the total button count-how many ones will be drawn et..
for(int x=0;x<someCount<x++){
for(int y=0;y<anotherCount;y++){
//needed frame&name ect assingments
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];}}
};
...
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
dispatch_async(drawingQue, ^{
self.buttonFramesBlock(234,someOtherInt);
});
}
First, never change UI outside of the main thread. So your should modify your code into something like:
dispatch_async(drawingQue, ^{
// ...
dispatch_async(dispatch_get_main_queue(), ^{
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];
});
});
Second, never change UI inside the method shouldAutorotateToInterfaceOrientation. All you have to do inside that method is return whether the view should rotate or not. For instance, in some cases where you have a view controller hierarchy, the view might not get rotated even if you return YES in shouldAutorotateToInterfaceOrientation.
So, you should call your code inside the method:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
This can be achieved in many ways. The simplest (and the one I recommend) is to use a standard Objective-C method:
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) { // landscape
[self rotateButtonWithA:234 b:24];
} else { // portrait
[self rotateButtonWithA:123 b:24];
}
}
- (void)rotateButtonWithA:(CGFloat)a b:(CGFloat)b
{
dispatch_async(drawingQue, ^{
// ...
dispatch_async(dispatch_get_main_queue(), ^{
button.frame= CGRectMake(x+y, x-y, a, b);
[button setTitle:#"abc"];
});
});
}
You don't really need to call the block itself from multiple places. But if you still want to do that, there are many answers here that show you how to do that.

Pass Button object to method

I am trying to write a generic method that would allow me to pass the following: "x", "y" "object" and then have it move. Currently I have this:
-(void) changeObjectLocations: (integer_t) xSpot: (integer_t) ySpot: (id) sender {
if (![sender isKindOfClass:[UIButton class]])
{
UIButton *myObject = (UIButton *)sender;
[UIView animateWithDuration:1.5
animations:^{
CGRect newFrame = myObject.frame;
newFrame.origin.x = xSpot;
newFrame.origin.y = ySpot;
myObject.frame = newFrame;
}];
}
else if (![sender isKindOfClass:[UILabel class]])
{
UILabel *myObject = (UILabel *)sender;
[UIView animateWithDuration:1.5 animations:^{
CGRect newFrame = myObject.frame;
newFrame.origin.x = xSpot;
newFrame.origin.y = ySpot;
myObject.frame = newFrame;
}];
}
}
I then want to call it like so:
-(void) orientationBlockLandscape {
[self changeObjectLocations: 456 :282 : btn1] ;
[self changeObjectLocations: 391 :227 : lblTitle] ;
}
Although it is working, on compile I get the following warning:
SecondViewController.m:33: warning: 'SecondViewController' may not respond to '-changeObjectLocations:::'
Is there a better way I can/should be passing the object? Thanks in advance for any and all help.
Geo...
Based on the warning outputted, it sounds like you didn't define changeObjectLocations in the header of your SecondViewController -- or it's not the same signature as what you've implemented.
The compiler looks for selectors which are the method definitions with the variables and return removed.
So a method like:
-(void) setObject:(id) anObject forKey:(NSString *) keyname;
... would have a selector of:
setObject:forKey:
Therefore, your method of:
-(void) changeObjectLocations: (integer_t) xSpot: (integer_t) ySpot: (id) sender
... will have a selector of:
changeObjectLocations:xSpot:ySpot:
Note that the parameter names are part of the selector so:
changeObjectLocations:xSpot:ySpot:
and
changeObjectLocations:::
.. are two entirely separate selectors which represent two entirely separate methods.
Although it is legal in the language to use parameters without names e.g. ":::" it is very, very poor practice largely because it is to easy to get a naming collision. Being explicit not only makes the code easier to read and maintain but makes it easier for the complier and runtime to function.

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];