Call +(void)method from -(IBAction) - objective-c

First of all, sorry if the title is misleading. This is literally my first ever creation with Xcode, so I may be a bit off with what everything is called and such.
So, I have a class with a few IBAction's, something like this:
// AppController.h
#import <Foundation/Foundation.h>
#interface AppController : NSObject {
}
+ (void)addItem;
- (IBAction)addButton:(id)sender;
#end
And:
// AppController.m
#import "AppController.h"
#implementation AppController
+ (void)addItem {
NSLog("Action");
}
- (IBAction)addButton:(id)sender {
[[self class] addItem];
}
When I run the app and click the button that fires the IBAction, the app hangs and the Xcode-window becomes selected, with a green "breakpoint" on the line NSLog(...).
I have no idea what this reaction is called, nor what I am supposed to do about it. Any help is appreciated!

NSLog("Action");
The first argument of NSLog() must be an NSString, not a C string (char *). You missed the # character:
NSLog(#"Action");
is the correct approach.
Edit: if it's just a typo, and after fixing it, the problem still persists: then you most likely set a breakpoint on that particular line. Delete or disable the breakpoint to enable the program to continue.

Related

Preventing a crash if a block is nil without thousands of ifs

I love using blocks. I constantly create custom classes that use blocks to communicate with callers instead of delegate mechanisms.
But the problem is that these classes got polluted with checks to see if the block was declared before running them. Something like:
if (self.onExit) {
self.onExit(flag);
}
obviously I cannot omit the if or the code will crash if _onExit is nil;
Is there something I can create, a category or something, that will allow me to just run the block directly without the thousands of ifs but will internally check for nil before running it?
What about a macro like this?
#define BLOCK_SAFE_RUN(block, ...) block ? block(__VA_ARGS__) : nil
This is from another answer with more details here: https://stackoverflow.com/a/13037198/747339
rmaddy suggested this in a comment and I followed up with an example: a little bit of refactoring might be the way to go here. Put the null-check-and-call into a method. Then call the method unconditionally wherever you have the conditional call of the Block.
Something like this:
#interface MyClass : NSObject
#property (copy, nonatomic) void (^onExit)(BOOL);
#end
#interface MyClass ()
- (void)performExit:(BOOL)flag;
#end
#implementation MyClass
- (void)performExit:(BOOL)flag
{
if( self.onExit ){
self.onExit(flag);
}
}
- (void)doThatThing
{
// Replace
// if( self.onExit ){
// self.onExit(flag);
// }
// with
[self performExit:YES];
// everywhere.
}
#end

Multiple parameters in method issue

My first shot at creating a method with multiple parameters. Still trying to wrap my head around how Objective C does things. Been banging my head for a couple days on this now. Finally ready to ask for help. Searched and tried many posts here on stack overflow. Below is various code chunks I'm working with ... this is a cocos2d v3 project FYI.
// MainPlayScene.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#include <sys/sysctl.h>
#interface MainPlayScene : CCScene <CCPhysicsCollisionDelegate>
+ (MainPlayScene *)scene;
- (id)init;
- (void)evaluateTileAttack:(CCNode*)tileTouchedCCNode : (CCNode*)tileTouchedCCNode2;
#end
// MainPlayScene.m
#import "cocos2d.h"
#import "MainPlayScene.h"
#implementation MainPlayScene
{
CCNode *tileTouchedCCNode;
CCNode *tileTouchedCCNode2;
}
+ (instancetype)scene
{
return [[self alloc] init];
}
- (id)init
{
return self;
}
- (void)evaluateTileAttack: (CCNode*)ccnode1 : (CCNode*)ccnode2
{
NSLog(#"ccnode1: %#", ccnode1.physicsBody.collisionType);
NSLog(#"ccnode2: %#", ccnode2.physicsBody.collisionType);
}
- (void)actionMenuAttackHandler: (id)sender
{
[self evaluateTileAttack: tileTouchedCCNode, tileTouchedCCNode2];
^^^^^^^^^^^^^^^^^^^^^
error at this line
}
#end
ERROR: No visible #interface for 'MainPlayScene' declares the selector 'evaluateTileAttack:'
Not sure why I am getting this error because I think I am declaring in MainPlayScene.h properly ...
The method declaration, though technically valid I think, is at least unusual for ObjC. Best seen when you wrap and align (as is customary for long method calls/declarations) on the colon:
- (void)evaluateTileAttack:(CCNode*)tileTouchedCCNode
:(CCNode*)tileTouchedCCNode2;
Normally a method has a name for all parameters:
- (void)evaluateTileAttack:(CCNode*)tileTouchedCCNode
otherNode:(CCNode*)tileTouchedCCNode2;
The call is definitely invalid, ObjC methods do not take a comma-separated list of parameters (unless specifically declared to do so, which is rare). So this is illegal:
[self evaluateTileAttack: tileTouchedCCNode, tileTouchedCCNode2];
Instead it should be (not sure about this unnamed format though):
[self evaluateTileAttack:tileTouchedCCNode
:tileTouchedCCNode2];
This definitely works and is the expected/recommended approach:
[self evaluateTileAttack:tileTouchedCCNode
otherNode:tileTouchedCCNode2];

outsourcing code

My iPhone game has a lot of recurring code (move pictures, add score), that makes it too big when repeating the same code on each button click.
this is ViewController.m
interface and implementation between Viewcontroller.h and ViewController.m is correct - workes well
- (IBAction)button_xx_pressed:(id)sender
{
//trying to call the outsourced code
[self this_is_a_test];
}
so I tried to make outsourced recurring code. I don't need method or functions that gives a result back or something. Just do some action like NSLog output...(just a test). Or in the original version - move pictures, add score and other stuff.
this is Outsourcing.h
#import "Outsourcing.m"
#end
this is Outsourcing.m
#import "Outsourcing.h"
- (void)this_is_a_test {
int test_variable = 999;
NSLog(#"Give me a test output: = %i", test_variable);
}
#end
this would shrink the size of my game more than 80% (very important). I have thousands of recurring programming lines and at the moment, I don't know how to handle it.
actual error messages:
Outsourcing.h => missing context for method declaration
Outsourcing.m => missing context for method declaration
=> #end must appear in Objective-C context
Anyone any hints for me? Thank you very much... The rest of my game is ok... everything would run without issues. I'm very glad that I got it running (but the game size is a problem).
1 or 2 months ago, I never used xcode before. I just had some experience in VBA. And what I want is similar to.
=> Call this_is_a_test
=> Private Sub this_is_a_test()
But it seems I'm too stupid :-(
thanks
#interface Outsourcing : NSObject
- (void)this_is_a_test;
#end
#import "Outsourcing.h"
#implementation
- (void)this_is_a_test {
int test_variable = 999;
NSLog(#"Give me a test output: = %d", test_variable);
}
#end
and you call it like this in your ViewController:
#import "Outsourcing.h"
...
- (IBAction)button_xx_pressed:(id)sender
{
Outsourcing *outsourcing = [[[Outsourcing alloc] init] autorelease];
//trying to call the outsourced code
[outsourcing this_is_a_test];
}
You are missing
#interface Outsourcing : NSObject
in your header file (Outsourcing.h). Remove:
#import "Outsourcing.m"
You import header files, not source files....You are also missing:
#implementation Outsourcing
In your .m file, just after the import declaration.

Property/Synthesize errors

Sorry to keep asking basic questions here but I don't know where else to go. Wrote some code with a slider, textfield and buttons for incrementing the slider to demonstrate key value coding. Everything worked find. The next step was to use 'property' and 'synthesize' in place of the accessor and setter methods;
#import <Foundation/Foundation.h>
#interface KVCController : NSObject {
int fido;
}
#property(readwrite, assign) int fido;
#end
~~~~~
#implementation KVCController
#synthesize fido;
- (id)init{
self = [super init];
if (self) {
// Initialization code here.
[self setValue:[NSNumber numberWithInt:5] forKey:#"fido"];
NSNumber *n = [self valueForKey:#"fido"];
NSLog(#"fido = %#", n);
}
return self;
}
~~~~~~~
#end
I get an incomplete implementation error on #implementation KVCController. If I put the get and set methods for 'fido' in it clears up.
The second error occurs with #synthesize fido;. It says property must be declared in the implementation. Everything is copied correctly out of the book and near as I can tell, it looks just like all the other uses of property and synthesize I have looked at. Anyone have any ideas on what I am missing or doing wrong?
Xcode 4.1 automatically creates a delegate class which I usually ignore if I am not working on delegates. I created my own class for the KVC exercise and just added the property/synthesize declarations to it with appropriate modifications and got the errors. I just put the property/synthesize declarations into the delegate class, moved my IBAction code to the appropriate places, redid the bindings, and erased the class I created and everything worked fine. Do property/synthesize declarations need to be treated like delegate material?
incomplete implementation means you have a -(void)something that may be defined in your header that you are not using in your #implementation. Make sure that you do not have any unused methods listed in your header. if you do, either remove them from the header, or create the method in your implementation.
- (void) dosomething
{
/* blank for now */
}
if you have -(void)dosomething in your implementation, define it in your header.
#import <Foundation/Foundation.h>
#interface KVCController : NSObject {
int fido;
}
#property(readwrite, assign) int fido;
- (void) dosomething;
#end

Cocoa: Calling a method from AppDelegate.m

In order to better understand the startup, event queue, and methods within my application I'm trying to write a program that does two things: Play a beep at the startup and every time the user hits a button. So far it only plays when the user hits the button. I know there may be multiple ways to get the startup beep to play, but in order to work with initialization code I want to do it by calling my beep method from within the applicationDidFinishLaunching method of the AppDelegate.m file.
Here is my code:
Log.h
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m
#import "Log.h"
#implementation Log
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[self beepAndLog];
}
#end
And the applicationDidFinishLaunching method looks like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[Log beepAndLog];
}
In the applicationDidFinishLaunching method, however, XCode warns me that
'Log' may not respond to '+beepAndLog'
and indeed, there is no beep and the log reads as follows:
MethodResponse[11401:a0f] +[Log
beepAndLog]: unrecognized selector
sent to class 0x100002100
("MethodResponse" is the name of my project, btw)
I'm unsure why Log wouldn't respond to beepAndLog, seeing as that's one of its methods. Am I calling it incorrectly? I have a feeling this will be painfully obvious to you more experienced people. I'm a newbie. Any help would be appreciated! Thanks!
There are two possibilities. Either you defined beepAndLog as an instance method, when you wanted a class method, or you want to call it on an instance when you called it on the class.
To change it to a class method, change the header to read:
+(void)beepAndLog;
and the implementation:
+(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
For the other solution, make sure you have an instance of class Log around (probably a singleton), and do something like:
[[Log logInstance] beepAndLog];
from your notification method. The Log class would need to look something like this:
Log.h:
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
+(Log *)logInstance;
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m:
#import "Log.h"
Log *theLog = nil;
#implementation Log
+(Log *)logInstance
{
if (!theLog) {
theLog = [[Log alloc] init];
// other setup (like hooking up that IBAction)
}
return theLog;
}
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[[Log logInstance] beepAndLog];
}