Variable out of IBAction - objective-c

I have problem that im trying to get solve for like week.
My goal is to get variable out of my IBAction, to use for example in -(void)viewDidLoad..
But as far as I am now I can use my variable only in my IBAction..
- (IBAction) changeLat:(NSNumber *)str {
longi = str;
double lop = longi.doubleValue;
NSLog(#"%f",lop);
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog (#"%#",lop);
}
It NSLog shows everything fine in action, but in view did load it doesn't even recorganize it.

If you create a variable inside of -IBAction, the scope of that variable is only that method, so you cannot access to that variable outside it.
If you want your variable to be global to your class, you have to create it in the declaration of your class, like this:
#interface MainViewController () {
#private
double lop;
}
Put this at the beginning of your .m file, and then lop would be accesible in all your class.
You can read more about the scope of the variables here:
http://www.techotopia.com/index.php/Objective-C_Variable_Scope_and_Storage_Class

Actually, IBAction is converted to void by the preprocessor. It's used by Interface Builder as a label that identifies this method as an action able to be related from an IB Object.
There's no way (AFAIK) to use two return types in a function (for example `(IBAction double)´, equivalent to ´(void double)´), but a good practice could be something like this:
- (IBAction)changeLatAction:(id)sender {
NSNumber *str = <get the NSNumber from a valid place>;
[self changeLat:str];
}
- (double) changeLat:(NSNumber *)str {
longi = str;
double lop = longi.doubleValue;
NSLog(#"%f",lop);
return ????;
}
Your first declaration of changeLat seems to be wrong, because as a first parameter you'll always get the "sender" or "caller" object, related from IB (when called from an action, of course), so, you need to get the str value from a valid place.
Cheers.

Related

Cannot keep track of the value of my property in Objective-c

I have a problem with one property in one class. I am coding with iOS 6.1 if it makes any difference.
The class is UIViewController and the property is the declared in the header file like so:
// Keeps track of time in seconds
#property (nonatomic, strong) NSNumber *timeInSeconds;
In my implementation file I use the property during 3 occasions:
one is to add time with the method - (void)addTime
one is to subtract time with the method - (void)subtractTime
Those two methods use the property like so:
- (void)addTime
{
CGFloat timeFloat = [self.timeInSeconds floatValue];
// Here I set the value of the property timeInSeconds, but I can't access that value later on in the code!
self.timeInSeconds = [NSNumber numberWithFloat:timeFloat +5];
NSLog(#"Total Time:%#", self.timeInSeconds);
}
The two methods addTime and subtractTime do what they are supposed to do, and they keep a good track of the property timeInSeconds value as I add then subtract then add...
The problem is when I call in the same implementation file the third method which is:
- (void)updateLabelTime
{
self.label.attributedText = [[NSAttributedString alloc]initWithString:[self.timeInSeconds stringValue]];
[self.label setNeedsDisplay];
[NSTimer scheduledTimerWithTimeInterval:0.8 target:self selector:#selector(updateLabelTime) userInfo:nil repeats:YES];
}
I also tried to create a the NSAttributedString with stringWithFormat instead of initWithString but the problem persists which is that instead of returning the value of the property timeInSeconds which i previously set using addTime and subtractTime, it calls the getter which creates a new instance of timeInSeconds since in my getter I have lazy instantiation.
I tried to not write the getter/setter for the property (since I am using iOS 6.1) but it makes no difference.
If I just set the label to some random string, it would work. The problem is that if I know the value of timeInSeconds is 55, it would still create a new _timeInSeconds.
I tried my best with my English since I am French, please don't answer if the question was already asked by a beginner iOS developer and just redirect me. I couldn't find an answer though, thanks!
EDIT: Here is the custom getter
- (float)timeInSeconds
{
if (!_timeInSeconds) {
_timeInSeconds = 0;
}
return _timeInSeconds;
}
SECOND EDIT:
The stupid beginner mistake that I made was that addTime and subtractTime are actually implementing a protocol and they set the property which "lives" in another class which is why I could not access it! That other class that needs the protocol was creating a new instance of the class where addTime and subtractTime is written.
What needs to be done is to set the controller as the delegate for the protocol. I did this in the viewDidLoad method with something like:
self.view.delegate = self;
Thanks for all the help.
In your header file, declare this property:
#property (assign) float timeInSeconds;
In the implementation file:
#synthesize timeInSeconds = _timeInSeconds;
- (void)viewDidLoad
{
[super viewDidLoad];
_timeInSeconds = 0.0f;
}
- (void)addTime
{
_timeInSeconds += 5.0f;
}
This should initialize timeInSeconds to zero and then increment its value by 5 each time you call addTime. To use its value in the label:
- (void)updateLabelTime
{
self.label.text = [NSString stringWithFormat:#"%f", _timeInSeconds];
}
In your custom getter you are assigning a scalar value to an object property. In fact assigning zero to an object property is the equivalent of setting the object to nil.
What you need to do is this:
- (float)timeInSeconds
{
if (!_timeInSeconds) {
_timeInSeconds = [NSNumber numberWithFloat:0.0f];
// or alternatively with the latest version of objective c
// you can more simply use:
// _timeInSeconds = #(0.0f);
}
return _timeInSeconds;
}

"unused variable"

#implementation demoScene{
-(void) initializeScene {
moon *_moon=[[moon alloc]init];
}
-(void) updateBeforeTransform: (CC3NodeUpdatingVisitor*) visitor {
deltaTime =deltaTime+visitor.deltaTime;
NSLog(#"delta time=%0.12f",deltaTime);
[_moon print:deltaTime/100000];
}
#end
Here is my problem.
I want to create an object from moon class in initializeScene method and I want to send message to that object in updateBeforeTransform method.
When I type the code like this, I can not send message to _moon object and get "unused variable" warning message.
I know the object is out of scope but if i need to send message from updateBeforeTransform method. And updateBeforeTransform method is called like 60 time in a second. So I did not want to create an object 60 times in a seconds.
Any suggestion would be appreciated.
You need an instance variable instead of creating a new variable in the initializeScene method:
#implementation demoScene {
moon *_moon; // You may already have this in the .h file - just have it in 1 place.
}
- (void)initializeScene {
_moon = [[moon alloc] init]; // assign to ivar
}
- (void)updateBeforeTransform:(CC3NodeUpdatingVisitor*) visitor {
deltaTime = deltaTime + visitor.deltaTime;
NSLog(#"delta time=%0.12f", deltaTime);
[_moon print:deltaTime / 100000];
}
#end
Side note - Class names should begin with uppercase letters. Variables and method names begin with lowercase letters.

Objective-C - Storing block with parameters for callback

I have a general routine, which takes a few parameters.
Something like:
-(id) doStuff:(int)A:(int)B:(int)C {
//doStuff
return object;
}
I have a UITableViewController, which houses a number of custom cells, each with their own ID. When 'Save' is hit, these cells are iterated and some cells need 'additional behaviour' when they are being saved.
Up to now, I've created a 'Callback' object, which stores an NSString * and a delegate in the custom cell. Upon being 'Saved', the cell looks, whether it has any callbacks to apply and uses
SEL sel = NSSelectorFromString(Sel);
if([Del respondsToSelector:sel])
[Del performSelector:sel withObject:Cell];
Now that works somewhat well..., however, it requires the method I pass to do a switch/case on the ID of the Cell that's passed, and I'd like to avoid that.
That's why I'd like to use blocks instead, but I don't really know how to store a parameterized block in a variable.
What I'm trying to do:
Declare a function block doStuff.
id (^doStuff) (int, int, int) = ^(int A, int B, int C) {
//does Stuff
};
And add the previously created block as callback
[Cell addCallback:(^doStuff)(1, 2, 3)];
The block must NOT be called at that moment, but stored in the cell and only called it when the time is right.
How would I go about this correctly?
Thank you very much.
Edit: What I'd also like to avoid is storing the parameters for the block in the cell and pass them upon calling, because that would require me to further specialize the cells unnecessarily.
It sounds like what you want is a block that calls your block, something like this:
[cell addCallback:^{ doStuff(1, 2, 3); }];
But this is a rather odd and convoluted design. It seems like there is probably a way to write it with only one block, but it's hard to give a solution that specific without a better idea of what you're doing.
The most straight forward way is to create a typedef containing how the block parameters should look like, then use it to declare a new property/ivar. The following sample code is copied from the Sensible TableView framework SCCellActions class:
typedef void(^SCCellAction_Block)(SCTableViewCell *cell, NSIndexPath *indexPath);
#interface SCCellActions : NSObject
...
#property (nonatomic, copy) SCCellAction_Block willDisplay;
...
#end
You could then set the property as follows:
cellActions.willDisplay = ^(SCTableViewCell *cell, NSIndexPath *indexPath)
{
cell.backgroundColor = [UIColor yellowColor];
};
Similarly, you could declare a parameter as follows:
...
- (void)callActionBlock:(SCCellAction_Block actionBlock)
{
if(actionBlock)
{
actionBlock(self.cell, self.cellIndexPath);
}
}
...
In which case the method should be called like this:
[myObject callActionBlock:^(SCTableViewCell *cell, NSIndexPath *indexPath {cell.backgroundColor = [UIColor yellowColor];}];
This answer is based on Chuck's suggestion and describes the pitfalls I encountered realizing it.
Creation:
Cell = [self CreateCell];
[Cell addCallback:^{ return doStuff(Cell, 1, 2, 3, 4) } At:ON_SAVE];
doStuff is a local block, declared before the cells. I was unable to add it directly to the cell, because I also needed a reference to the calling cell within the block.
Pitfall at this point: Class variables.
A block will only retain...or rather 'copy'...local variables, but not class variables.
Assuming that 'Cell' was a class variable and set by 'CreateCell', the block would work with the value of Cell at the time the block is executed.
As such, it is important to remember to declare a local variable, which assumes the value of the class variable if necessary.
Storage:
- (void) addCallback:(CallBlock_t)B At:(int)at {
//Creates a Callback-Object and passes it the block and adds it to an Array.
}
- (id) initWithBlock:(CallBlock_t)B At:(int)at {
self = [super init];
if(self) {
Block = [B copy]; //Yes, Copy. Not retain.
When = at;
}
return self;
}
Pitfall at this point: If the block is merely retained, the local block from the calling function will go out of scope and the program will fail with 'Bad Access'. Copy resolves this problem.
Of course you need to release the Block once you're done using it (in the dealloc of the callback class), but that's a given.
I hope this little explanation will save someone some grief.

Retrieving data from singleton in a more clever way ?

I have some game data in my GameStateSingleton, which I don't want to retrieve every time explicitly with [[GameStateSingleton sharedMySingleton]getVariable], so I asked myself whether it is possible to do something like that :
In the interface file of my class, GameLayer I set up properties and variables like sharedHealth.
#interface GameLayer : CCLayer
{
int sharedHealth;
}
#property (nonatomic,assign) int sharedHealth;
and of course synthesize it in the implementation.
#synthesize sharedHealth;
In the initialization of GameLayer I would like to do something like :
sharedHealth = [self getCurrentHealth];
and add the corresponding method
-(int)getCurrentHealth{
int myHealth = [[GameStateSingleton sharedMySingleton]getSharedHealth];
return myHealth;
}
Is that possible ? From what I have experienced, I just seem to get crashes. How would I achieve my goal, to not always have to call the long method, as it always retrieves the same variable? There has to be a solution for this ...
You don't need an instance variable for that. You could just write a shortcut function like this:
- (int)sharedHealth {
return [[GameStateSingleton sharedMySingleton] getSharedHealth];
}
And where ever you need that value, you call [self sharedHealth].
You could also use a preprocessor macro instead. Just define this:
#define SharedHealth [[GameStateSingleton sharedMySingleton] getSharedHealth]
And then simply use that when you need the value.
Note, that in Objective-C you don't call getter methods "getVariable", but simply "variable". Mostly this is a convention, but if you start using KVC or KVO it's a rule you have to follow. So it's better to get used to it as soon as possible.
If it's just the repetitive typing that you're trying to avoid, you could use the old C way...
#define GAME_STATE [GameStateSingleton sharedMySingleton]
...and then...
int localValue = [GAME_STATE property];

How do I write to an NSObject from within a C function that doesn't see Obj-C variables?

I'm trying to get some code going that lets me display raw trackpad data from my macbook pro, like the app FingerMgmt. Unfortunately, no one seems to have the source for FingerMgmt. I did find some other source code that kind of works, however. I was able to NSLog the data I wanted to see like this:
int callback(int device, Finger *data, int nFingers, double timestamp, int frame) {
for (int i=0; i<nFingers; i++) {
Finger *f = &data[i];
NSLog(#"Frame %7d: Angle %6.2f, ellipse %6.3f x%6.3f; "
"position (%6.3f,%6.3f) vel (%6.3f,%6.3f) "
"ID %d, state %d [%d %d?] size %6.3f, %6.3f?\n",
f->frame,
f->angle * 90 / atan2(1,0),
f->majorAxis,
f->minorAxis,
f->normalized.pos.x,
f->normalized.pos.y,
f->normalized.vel.x,
f->normalized.vel.y,
f->identifier, f->state, f->foo3, f->foo4,
f->size, f->unk2);
//todo-get data from raw C to obj-C variable
}
return 0;
}
But whenever I try to store any of the data to an Obj-c string or variable, the C code does not see the variable as having been declared. Because of this, I cannot write to any text fields or graphical displays in Obj-C, and I cannot store the data to a variable that Obj-c can access.
Basically, I need a way to write to an Obj-C variable or object from within the callback.
On a side note, I had a very similar problem with an iPhone app a while back, and I ended up fixing it by somehow declaring the app delegate within the C code and writing to or reading from the variable like this-
me.delegate=(id <UIApplicationDelegate,UITabBarControllerDelegate>)[[UIApplication sharedApplication] delegate];//allows access to the delegate within C function
me.delegate.number0=5;//writes to this variable in the delegate
For some reason, I can not seem to adapt this to my current situation. I always get the error that "me" is undeclared.
A Objective-C method can access instance variables because it is automagically passed a hidden parameter with the public name self - any reference to an instance variable, say fred, is translated by the compiler into a field reference, say self->fred (and a similar translation for property references).
For your C function callback to access the fields of any object (or call an object's methods) you need to pass the function a reference to the object. Two simple ways:
Add an argument to the function. Many C callback protocols include a general "user defined" values which is passed around as void *, if you are calling one of these pass your object reference as this value and cast it within the C function back to the correct Objective-C type.
Pass the object via a global (or file static) variable, e.g. static NSSomeType *objectForCallback;. This method works when you're stuck with an existing C callback protocol which doesn't support a user defined value. However it is not thread or re-entrant safe as you are sharing a single static variable.
In both cases make sure the objected is retain'ed if you're not using garbage collection.
In response to comment
Case 1: You will see C functions declared which (a) take a callback function and (b) a user-defined value to pass to that function on every call. For example:
typedef T ...;
T findMatching(T *buffer, // an array of T to search
size_t count, // number of items in array
int (*matcher)(T item, void *user), // user function for match, 1st arg is item, 2nd user-supplied value
void *userValue); // user-supplied value to pass to matcher
If you are faced with C function like this you can pass a (retain'ed if needed) Objective-C object as userValue and cast it back to its Objective-C type inside matcher. For example:
int myMatcher(T item, void *user)
{
NSMutableDictionary *myDictionary = (NSMutableDictionary *)user;
...
}
- (void) someMethod
{
NSMutableDictionary *sharedWithC = ...;
...
T found = findMatching(buffer, count, myMatcher, (void *)sharedWithC);
...
}
Case 2: Objective-C is (a superset of) C. You declare a global just as you would in C. For example (little checking, not thread safe):
static NSMutableDictionary *myGlobalDictionary = nil; // "static" makes the variable only visible to code in the same file
- (void) setupTheSharedDictionary
{
myGlobalDictionary = [[[NSMutableDictionary alloc] init] retain];
}
- (void) releaseTheSharedDictionary
{
if(myGlobalDictionary != nil)
{
[myGlobalDictionary release];
myGlobalDictionary = nil;
}
}
In response to second comment
I'm guessing you are trying to use some third party (Google?) code. That code defines a callback protocol - a C function type. You cannot just redefine that C function type adding an extra argument and expect the third party code to magically cope!
So unless you intend to change the C you can use the second approach - store the reference to Objective-C object in a global. In your case this will be something like:
static MT2AppDelegate *sharedWithCAppDelegateReference;
int callback(...)
{
...
[sharedWithCAppDelegateReference->L1 setStringValue:#"Hellofff"];
...
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
sharedWithCAppDelegateReference = self; // store so C can pick it up
...
MTRegisterContactFrameCallback(dev, callback);
...
}
But remember this is not thread or re-entrant safe - you are effectively passing a function parameter via a global variable. If you need it to be thread/re-entrant safe you need to get a bit more involved.