I have a method that draws a one sprite on the screen with the animation effects,
if I call in init this method
if( (self=[super init]) ) {
// ....
[self myMethod];
// .....
}
Then he does it once on my project
When I call by schedule
-(void)schedulMyMethod:(ccTime)dt {
[self myMethod];
}
if( (self=[super init]) ) {
// ....
[self schedule:#selector(schedulMyMethod:) interval:0.5];
// .....
}
It runs for an unlimited times
I need so that I can call the my method some amount
You mean, you want to have it repeat N times? You'll need to keep state on times remaining for it to run.
#property (nonatomic, assign) NSInteger timesToRunMyMethod;
- (void)beginRunningMyMethod {
self.timesToRunMyMethod = 100; // N==100
[self myMethod];
}
- (void)myMethod {
self.timesToRunMyMethod--;
// do stuff
if (self.timesToRunMyMethod > 0) {
// i used native delayed execution, you can replace it with whatever cocos2d offers if you want
[self performSelector:#selector(myMethod) withObject:nil afterDelay:0.5];
}
}
And it's probably wrong to start this on init. Is it a view controller? Then you can use viewDidAppear or willAppear.
Related
I have a block function defined as the below:
#property (atomic, assign) bool callInProgress;
//in implementation:
- (void)synchronize:(void(^)(void(^unlock)()))block {
if (!_callInProgress) {
_callInProgress = YES;
[_tableView setScrollEnabled:false];
block(^{
[_tableView setScrollEnabled:true];
_callInProgress = NO;
});
}
}
Then when I do:
[self synchronize:^(void(^unlock)()) {
}];
and I set a break point at that [self synchronize..], the break point gets hit twice no matter what! If I add a body:
/*break point on this line*/ [self synchronize:^(void(^unlock)()) {
NSLog(#"HERE");
unlock();
}];
HERE gets printed ONCE but the break point gets hit twice!
Any ideas why?
The breakpoint is being hit once when you reach the synchronize call, and once when you enter the callback block. Both are on the same line.
I am currently working on an implementation where many blocks are used. Every block needs to communicate with self.
Currently I am doing this:
#implementation Foo
- (void) bar
{
__weak Foo *weakSelf = self;
[self doBlockStuff:^(id something) {
[weakSelf doSomething];
}];
}
#end
I have many functions that do the same with the weak instantiation.
Is it right to instantiate the weak property once in the interface block and use it everywhere?
It's working but is it an accepted practice?
#interface Foo ()
{
__weak Foo *_weakSelf;
}
#end
#implementation Foo
-(instancetype) init
{
self = [super init];
if(self) {
_weakSelf = self;
}
return self;
}
- (void) bar1
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
- (void) bar2
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
- (void) bar3
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
- (void) bar4
{
[self doBlockStuff:^(id something) {
[_weakSelf doSomething];
}];
}
#end
Edit after Testing with new Informations:
I did wrote a little test case and now i can demonstrate why the second one is not working.
In my Testclass a imake a dispatch after 5 seconds with the relevant self usage and i logged when my dealloc was called.
#implementation Foo
- (void)dealloc
{
NSLog(#"dealloc");
}
- (instancetype)init
{
self = [super init];
if (self) {
}
return;
}
- (void)bar
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self doSomething];
});
}
#end
If the class loses the holder, because the controller is closed or whatever and the function is still running, the class will dialoged after the dispatch is done.
#interface Foo ()
{
__weak Foo *_weakSelf;
}
#end
#implementation Foo
- (void)dealloc
{
NSLog(#"dealloc");
}
- (instancetype)init
{
self = [super init];
if (self) {
_weakSelf = self;
}
return;
}
- (void)bar
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_weakSelf doSomething];
});
}
#end
This one will also on dealloc if the dispatch is done. Because the _weakSelf property is still holing by the class, a shorthand for using self->_weak. Self means self :)
#implementation Foo
- (void)dealloc
{
NSLog(#"dealloc");
}
- (instancetype)init
{
self = [super init];
if (self) {
}
return;
}
- (void)bar
{
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf doSomething];
});
}
#end
this one will dealloc immediately because the weak reference is only existing in this function given to the block. The function is over and if the class loses his reference the block has no property that is holing anyone. But the weak property is still usable when the reference class is available.
To be sure, that this weak property will be alive, we can set a strong cycle in the block.
This doesn't at all do what you think it does. That __weak instance variable in those methods? That's just a shorthand for self->_weak. All of those methods using the proposed manner still capture self strongly.
Stick to what you were doing before.
This is really bad then if the __weakSelf still holds a strong reference. The question is that if instantiated in the method and used in the method does it still really have a weak anchor to self or is it holding a strong reference in that very moment. Based on the documentation you can instantiate a weak reference outside the block and even make it strong inside the block if you want. Take a look here
http://aceontech.com/objc/ios/2014/01/10/weakify-a-more-elegant-solution-to-weakself.html
my question at this point in time is that why does a weak self instantiated dealloc it's self when the real self deallocs..? Because I tried to hold the weak self in another viewController and then dealloc the real self. But the weak self was dealloc as soon as I dealloc the real self.
I have an issue at the moment. I am making a game. When two imageViews collide the game will end. I am using CGRECTIntersects rect to detect if the two images collide.
The issue is that when i restart the game the collision will happen when in fact the two images have not actually touched one another?
Any suggestions would be much appreciated.
Thank you
-(void)collision {
if (CGRectIntersectsRect(object.frame, object2.frame)) {
object.hidden=YES;
object2.hidden=YES;
retry.hidden=NO;
[timer invalidate];
}
}
/- (void)viewDidLoad
{
retry.hidden=YES;
timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(object2) userInfo:nil repeats:YES];
objectSpeed1 = CGPointMake(3.0, 2.0);
[super viewDidLoad];
}
/- (IBAction)retry:(id)sender {
[self performSegueWithIdentifier:#"restart" sender:sender];
}
-(void)object2 {
object2.center = CGPointMake(object2.center.x + object2.x, object2.center.y + objectspeed1.y);
if (object2.center.x > 310 || object2.center.x < 10) {
objectspeed1.x = - objectspeed1.x;
}
if (object2.center.y > 558 || object2.center.y < 10) {
objectspeed1.y = - objectspeed1.y;
}
Just consider this case on the code. You can check if image views are on their start position or they came to this position from another. It's enough to keep BOOL property on viewcontroller's class like this:
// ViewController.m
#interface ViewController ()
#property (readonly) BOOL isStarted;
#end
#implementation
- (void)viewDidLoad {
[super viewDidLoad];
_isStarted = NO;
// Make some preparations for app's needs
_isStarted = YES;
}
#end
_isStarted is a flag which shows whether viewcontroller is ready to handle the data.
if(_isStarted) {
// Analyze image views' frame
}
else {
// Do nothing
}
I have object with properties and when I change some of properties, I do a lot of calculations. If I change one property, then another, calculations execute 2 times. I want do all calculations 1 time. I see 3 solution:
Make method commit. But I don't wanna do excess methods.
Make method for change all properties. The same cause why I don't wanna do this. And what to do if I will have much more properties in future.
Make delay after delay commit changes. I really do not know what consequences this may turn into. Something like this:
.
#interface TestObject : NSObject
#property (nonatomic, assign) float x;
#property (nonatomic, assign) float y;
#end
#implementation TestObject
{
BOOL flag;
}
- (id)init
{
self = [super init];
if(self){
flag = NO;
}
return self;
}
- (void)setX:(float)x
{
_x = x;
[self delayCommit];
}
- (void)setY:(float)y
{
_y = y;
[self delayCommit];
}
- (void)delayCommit
{
if(flag == NO){
flag = YES;
[self performSelector:#selector(commit) withObject:nil afterDelay:0];
}
}
- (void)commit
{
flag = NO;
NSLog(#"Do a lot of calculations....");
}
#end
Is third solution good practice? I want simple interface, I don't want excess methods.
If you really want a behavior like that, you should then have a timer that every N seconds, checks if any of the properties are changed, and does the computations. Something like:
- (void)setX:(float)x
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(_mySem, DISPATCH_TIME_FOREVER);
_x = x;
flag = YES;
dispatch_semaphore_signal(_mySem);
});
}
- (void)setY:(float)y
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(_mySem, DISPATCH_TIME_FOREVER);
_y = y;
flag = YES;
dispatch_semaphore_signal(_mySem);
});
}
- (void)updateCycle
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(_mySem, DISPATCH_TIME_FOREVER);
if(flag){
flag = NO;
NSLog(#"Do a lot of calculations....");
}
dispatch_semaphore_signal(_mySem);
});
}
In your init method you would have to initialise the timer and the semaphore with
_mySem = dispatch_semaphore_create(1);
[NSTimer scheduledTimerWithTimeInterval:N
target:self
selector:#selector(updateCycle)
userInfo:nil
repeats:YES];
The way to use it would then just be:
[object setX:4.5f];
[object setY:0.3f];
Depending on how many properties you have, this may work for you instead:
- (void)setX:(float)x y:(float)y
{
_x = x;
_y = y;
[self commit];
}
Here, you set more than one property with one method call. You know you have all the values you need, so you can call commit directly.
I would suggest you not to try to be too fancy with delayed commits (unless you really have to) and to use commit as a separate call and perform your long calculation in a separate thread, e.g.:
- (void)setX:(float)x
{
_x = x;
}
- (void)setY:(float)y
{
_y = y;
}
- (void)commitChanges
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// ... Long calculations go here ...
dispatch_async(dispatch_get_main_queue(), ^{
// ... Once you done with your long calculation, put your UI code here
// ... (e.g. display results of your calculation in text box)
});
});
}
then from your main code will look like:
[obj setX:123.0f];
[obj setY:345.0f];
[obj commitChanges];
I'm trying to keep a timer running on another page when you switch to other pages and complete other tasks, in essence keeping a clock on how long it takes to do the tasks. Whenever I switch to another page, it resets the timer back to what it was started, and does the same with some switches on other pages that I'm trying to keep on. Any ideas?
Screenshot of storyboards:
Code so far:
//
// ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (IBAction)start{
ticker = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self ``selector:#selector(showActivity) userInfo:nil repeats:YES];
}
- (IBAction)reset{
[ticker invalidate];
time.text = #" 0:00";
}
- (void)showActivity{
int currentTime = [time.text intValue];
int newTime = currentTime + 1;
time.text = [NSString stringWithFormat:#"%d", newTime];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
// ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController{
IBOutlet UILabel *time;
NSTimer *ticker;
}
- (IBAction)start;
- (IBAction)reset;
- (void)showActivity;
#end
Your NSTimer is a member variable of your view controller class. I'm assuming that when you switch between views, you're destroying this view controller and instantiating an instance of a new one. That means this view controller is gone, as well as the timer; it's not that the timer is being reset, it's that your old timer has been destroyed an a new one is being created.
What you need is to store your NSTimer and its functionality in a place where it will not be destroyed every time you change your view controller. One solution is to create a Singleton class which handles the timer. (A Singleton class is a class that can only be created one time; only one instance of it can exist. You can read more about them here.)
Here is an example of how you can create a Singleton class in Objective-C. The header:
//ApplicationManager.h
#interface ApplicationManager : NSObject
+(ApplicationManager*) instance;
#end
And the implementation:
//ApplicationManager.m
#import "ApplicationManager.h"
#implementation ApplicationManager
static ApplicationManager* appMgr = nil;
+(ApplicationManager*) instance
{
#synchronized([ApplicationManager class])
{
if(!appMgr)
{
appMgr = [[self alloc] init];
}
return appMgr;
}
return nil;
}
+(id) alloc
{
#synchronized([ApplicationManager class])
{
NSAssert((appMgr == nil), #"Only one instance of singleton class may be instantiated.");
appMgr = [super alloc];
return appMgr;
}
}
-(id) init
{
if(!(self = [super init]))
{
[self release];
return nil;
}
return self;
}
#end
The first time you call the instance method, the instance of the ApplicationManager will be created. Each time you want to access it, call the instance method again; the ApplicationManager will be returned. Now you simply add your NSTimer (and any other object you wish to persist throughout your application) as member variables of the ApplicationManager class.
You then must import the ApplicationManager class into your view controller class, and your view controller methods will change to:
-(IBAction) start
{
[[ApplicationManager instance] setTicker:[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(showActivity) userInfo:nil repeats:YES]];
}
-(IBAction) reset
{
[[[ApplicationManager instance] ticker] invalidate];
time.text = #" 0:00";
}
-(void) showActivity
{
int currentTime = [time.text intValue];
int newTime = currentTime + 1;
time.text = [NSString stringWithFormat:#"%d", newTime];
}
If you want to make things nice and neat, you can also add this line to the top of your ApplicationManager class:
#define APPMGR [ApplicationManager instance]
Now instead of having to type [ApplicationManager instance] everywhere, you can simply refer to it as APPMGR instead. [APPMGR ticker] is a lot cleaner than [[ApplicationManager instance] ticker] :)