I'm newbie to Cocoa/Objective C. I've to change a value of a global NSSTring variable on every iteration of an NSTimer execution. I've declared the variable inside the appdelegate.m at the top of the file so it's global:
NSString *my_string = #"hello";
I call the NSTimer:
[[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(scan:) userInfo:nil repeats:YES] fire];
and inside scan i set the new value to my_string:
- (void) scan:(NSTimer *)timer{
//some execution
my_string = #"the new value";
}
but the variable value is always the same "hello", the content won't change.
Is it possible to do this? Solutions?
You do not need to call fire method, the scheduled timer will fire automatically after the specified interval.
Also, set a breakpoint at the scan: method to find out if it is called.
If you declare your my_string variable in the .m file then other files won't be able to see it (you #import the .h files not the .m). Do you do the timer stuff in the same file (appdelegate.m)?
I recommend not having global variables like this as it will often confuse things as the project builds up. You can have it either as an ivar with an accessor, or as static in the #implementation block with a static accessor so that you can have access to a unique instance from anywhere.
You can log the change to make sure it happens or set a breakpoint.
- (void) scan:(NSTimer *)timer{
//some execution
my_string = #"the new value";
NSLog(#"Changed my_string to %#", my_string);
}
Related
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.
So the short of it is I want to define a global string variable that I can reference whenever. The function that I reference it in, it returns a string. As soon as I store it and reference it in another function it outputs as <CGPath 0x5bbf50>
What the heck? The code is below and keep in mind this is a module for Titanium.
First, the definition of the global variable..
#interface ComTestModule : TiModule <CBCentralManagerDelegate, CBPeripheralDelegate>
{
NSString * teststring;
}
The next part is the function where I first send the string variable from titanium to xcode..
-(void)setService:(id)args{
ENSURE_ARG_COUNT(args, 2);
teststring = [args objectAtIndex:0];
NSLog(teststring);
}
The output of the NSLog displays the actual string that was passed.
Now the final function where I call the string again and attempt to output it to the log..
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
NSLog(#"---%#", teststring);
}
As I said before, during this step it outputs as ---<CGPath 0x3ef4e0>
I'm really not sure what's going on.. Any help at all about getting this to return as the original string instead of the CGPath would be great!
Effects like this typically happen when you store a pointer to an object that was released and deallocated. The runtime will then replace that chunk of memory that previously held an NSString instance with, in this particular case, a CGPath instance.
If you want to ensure that your NSString stays alive, you need to take ownership of it. You can do that by either retaining it or copying it. Copying is the preferred method when talking about strings, so try replacing this line:
teststring = [args objectAtIndex:0];
With this:
teststring = [[args objectAtIndex:0] copy];
Now just be sure to release it when you're done.
The other poster's suggestion was good. You might want to make your testString variable into a copied property:
In your .h file:
#property (nonatomic, copy) NSString teststring;
And in your .m file
#synthesize teststring;
Then when you assign to it, use code like this:
self.teststring = [args objectAtIndex:0];
That "dot syntax" invokes the setter for your property rather than changing the instance variable directly. Since you declared your property with the "copy" qualifier, the setter method copies the string before putting it into the instance var.
Finally, you would add this code to your dealloc method:
self.teststring = nil;
The setter method also releases any old value in the property before assigning a new value, so setting it to nil releases the old value.
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.
I need to know if I do it correctly. The application is running OK but I'm not sure I get the lifecycle correctly (leak ?).
Note: Instrument see no leak.
The code of a method aaa: of some class A:
- (void) aaa {
NSString *path = ...something...;
NSBlockOperation* theOp = [NSBlockOperation blockOperationWithBlock: ^{
// using path
[self somethingElseWith:path];
}];
[self.aQueue addOperation:theOp];
}
So I create a block to put on aQueue (NSOperationQueue*). The goal is to offload from the main thread the long running somethingElseWith: method, so that the GUI continue to be responsive.
Inside the block I reference the local var "path" that will be out of scope at the end of the aaa: method.
If I read the doc correctly, the block will do a retain on 'path'. But is ARC inserting a release at the end of this block implicitly ? Would be logical and nice.
Or should I declare 'path' as __block and assign it to nil at the end of my block ? (manual...)
Not sure I understand how to use __weak in this context.
The path variable is fine. You may however need to avoid a retain cycle by using a weak reference to self. If aQueue is a strong reference there may be a retain cycle causing self never to be released.
Solution:
- (void) aaa {
NSString *path = ...something...;
__weak id self_ = self;
NSBlockOperation* theOp = [NSBlockOperation blockOperationWithBlock: ^{
// using path
[self_ somethingElseWith:path];
}];
[self.aQueue addOperation:theOp];
}
Make sure the operation does not get called after the class should no longer exist.
The block will automatically handle memory management for any locals from the enclosing scope. You don't have to worry about retain/release pairs in this case. Note, though that path will be const within the block's scope. If you need pathto be mutable within the block, use the __block attribute.
The different ways a block handles variables is described in detail here: Blocks and Variables
My NSTimer (startTimer) works fine. It runs the selected method (runTimer) but whatever code I place in the (runTimer) it does not increment. For example if I run the code as below it prints out 5 times but does not increment x. Any ideas - thanks
- (void)startTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(runTimer:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:5]];
}
- (void)runTimer:(NSTimer *)aTimer {
int x;
x++;
NSLog(#"int x = %i",x);
}
You get a new x every time runTimer is called. If you make it static
static int x;
it'll act the way you expect. For cleanliness, I'd also initialize it:
static int x = 0;
Be careful with static variables in Objective-C methods as suggested by smparkes. They're shared between all instances of that class, so if you've got multiple instances of whatever object this code is from, his answer won't act the way you expect. You'd be better off with an instance variable, because each instance will have its own variable, without affecting other instances:
In your .h:
#interface MyClass : NSObject
{
int x;
}
Then in your -runTimer: method:
- (void)runTimer:(NSTimer *)aTimer {
NSLog(#"int x = %i", x++);
}
If you are guaranteed that there will only be one instance of whatever this class is (e.g. it's a singleton), a static variable inside the -runTimer: method will work, but I'd recommend using an instance variable or #property as it's better programming practice.
runTimer declares x every time, i.e. it creates a new variable, set to 0, increments it and prints it out, so it will always be the same value. you need a variable outside the scope of that method in order to increment it