I can't figure out what is wrong in code pasted below, im learing xcode with Todd Moore book, this is the chapter 2-hello pong, Im getting breakpoint 1.1 crash at scheduledTimerWithTimeInterval. In book at the end of timer configuration is added ] retain]; but xcode 5 says that's deperecated.I modificate this to compile without retain, but the problem is crash.
- (void)animate
{
_puck.center = CGPointMake(_puck.center.x + dx*speed,_puck.center.y + dy*speed);
}
- (void)start
{
if (timer == nil) {
timer = [NSTimer scheduledTimerWithTimeInterval:0.016 target:self selector:#selector(animate) userInfo:NULL repeats:YES];
}
_puck.hidden = NO;
}
Because you are in an ARC setting, automatic reference counting, the object that is doing the animate/start may not be in memory anymore depending on how the rest of the code worked.
If whatever object that is used to have a retain it may not be in memory when that timer gets called.
You may need to do a quick search for how to start a project without automatic reference counting so you can follow the examples in the book more directly.
someObject = [[something alloc] init] retain];
[someObject start];
If you are not storing someObject somewhere that is likely the problem.
[edit]
I found the source code for that Todd Moore example.
These changes should hopefully let it work with ARC.
get rid of this in PaddlesViewController.h
NSTimer *timer;
add (near other #properties)
#property (nonatomic, retain) NSTimer *timer;
in PaddlesViewController.m add (near other #synthesize)
#synthesize timer;
This should allow it to run without the retain
Related
I have this below error
-[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[1539]
It happens sometimes I try to tap several times on screen, because code is little, so all the code is pasted below
#interface ViewController ()
#property (nonatomic,weak) NSTimer *timer;
#property (nonatomic,strong)NSMutableArray * testArray;
#property (nonatomic,strong) dispatch_queue_t queue1;
#property (nonatomic,strong) dispatch_queue_t queue2;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.testArray = [NSMutableArray array];
_queue1 = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
_queue2 = dispatch_queue_create("test",DISPATCH_QUEUE_SERIAL);
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(addObjectforArray) userInfo:nil repeats:YES];
[timer fire];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
dispatch_async(_queue2, ^{
NSLog(#"touchesBeganThread:%#",[NSThread currentThread]);
NSArray * testTempArray = [NSArray arrayWithArray:self.testArray];
for (UIView *view in testTempArray) {
NSLog(#"%#",view);
}
});
}
- (void)addObjectforArray{
dispatch_async(_queue1, ^{
NSLog(#"addObjectThread:%#",[NSThread currentThread]);
[self.testArray addObject:[[UIView alloc]init]];
});
}
I can not understand why this happens, if I change _queue1 to DISPATCH_QUEUE_SERIAL, it becomes normal.
How can I understand this issue? If anyone could shed some light, that would be wonderful.
There are multiple problems in your code. They can cause all sorts of bugs randomly.
UIView should be created in the main thread using dispatch_get_main_queue().
https://developer.apple.com/documentation/uikit
For the most part, use UIKit classes only from your app’s main thread or main dispatch queue. This restriction applies to classes derived from
UIResponder
or that involve manipulating your app’s user interface in any way.
The property testArray is nonatomic but being accessed in two threads. The property should be atomic. It runs fine at this moment but it is fragile. If in the future testArray mutates, the app will crash randomly.
NSArray is not thread-safe. It should be locked while accessing in multiple threads or protected by other means.
As pointed out by #Nirmalsinh, the dispatch_async is redundant (actually harmful).
I am not sure if you have heavily simplified your code or only to test something. If you are not doing long running work, you might want to use dispatch_get_main_queue() in dispatch_async. It will save you from a lot of troubles.
It seems you are inserting nil value into your array. You cannot add nil to array or dictionary.
- (void)addObjectforArray{
NSLog(#"addObjectThread:%#",[NSThread currentThread]);
UIView *view = [[UIView alloc] init];
if(view != nil)
[self.testArray addObject:view];
}
There is no required to use a queue in the method. You are already using NSTimer for same.
Try to check above. It will help you.
I have an iOS App that provides test for users with time limit.
The test will span across multiple view controllers, where these view controllers may re-open during the test flow. I think of the following flow:
In AppDelegate.h, add a NSTimer and time spent in float:
#property (strong, nonatomic) NSTimer *timer;
#property (nonatomic) float timeSpent;
- (void)startTimer;
- (void)stopTimer;
Without forgetting #synthesize the above, make a start & stop timer function in AppDelegate.m:
- (void)startTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval: 0.1f target: self
selector: #selector(updateTime) userInfo: nil repeats: YES];
}
- (void)stopTimer {
[self.timer invalidate];
}
In the periodically-called updateTime function, increment the value of timeSpent by 1.
Last, in each View Controller, obtain the timeSpent value and format it with the format I want, such as "01min 56s", or "01:56".
Is there a simpler way to do so?
Note:
No Internet connection available, and the test will last for around 10 minutes only; thus using Google Analytics is an overkill & not applicable in this case
You are correct that you need one NSTimer and variable to track the global time and..
What you are proposing, using the app delegate as a glorified singleton will work..
But please don't, this is not very good practice. This blog post has a nice brief description on why, in my opinion at least.
http://www.cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html
Personally if it was me I would probably just use a dependency injection model and pass the NSTimer between the viewControllers. http://www.objc.io/issue-15/dependency-injection.html
In short, app delegate is probably the easiest and quickest way. But I would advise something a little more scalable if it is going to be anything other than a trivial app.
Hope this was useful :)
*Edit Sample code for singleton.
This should go at the top of singleton class to initialise it.
#interface
#property (nonatomic, strong) NSTimer *myTimer;
#end
#implementation MySingletonClass
+ (instancetype)shared
{
static MySingletonClass *_shared = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_shared = [[self alloc] init];
// any other initialisation you need
});
return _shared;
}
- (void)startTimer {
self.myTimer = [NSTimer scheduledTimerWithTimeInterval: 0.1f target: self
selector: #selector(updateTime) userInfo: nil repeats: YES];
}
- (void)stopTimer {
[self.myTimer invalidate];
}
then you can access it from any other classes in your program like this
#import "MySingletonClass.h"
//some method
- (void)myMethod
{
CGFloat currentTime = [MySingletonClass shared].globalTimeProperty;
// do something with the time
}
-(void)startTimer
{
[[MySingletonClass shared] startTimer];
}
-(void)updateTime
{
// do your update stuff here
}
Singleton header
#interface
#property (nonatomic, assign) CGFloat globalTimeProperty;
+ (instancetype)shared;
- (void)startTimer;
- (void)stopTimer;
#end
I may have missed some stuff but it should be enough to get you going.
I am new to iOS programming, and I could not find an answer out there already.
In Xcode 5, I am iterating over an array, and attempting to update a label with the values as they change.
here is the .h file...
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (strong, nonatomic) NSArray *currentNumber;
#property (strong, nonatomic) IBOutlet UILabel *showLabel;
- (IBAction)start;
#end
here is the main part of the .m file...
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.currentNumber = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", nil];
}
This is where it gets tricky...
The following works perfectly...
- (IBAction)start {
self.showLabel.text = [NSString stringWithFormat:#"new text"];
}
#end
As does this...
- (IBAction)start {
for (NSString *p in self.currentNumber) {
NSLog(#"%#", p);
sleep(3);
}
}
#end
But when I replace the NSLog with setting the .text attribute, it "fails". The timing still happens, and the label updates with the last item in the array after...
- (IBAction)start {
for (NSString *p in self.currentNumber) {
self.showLabel.text = [NSString stringWithFormat:#"%#", p];
sleep(3);
}
}
#end
And the last bit of weirdness, if I use the NSLog, and try to change the .text attribute before the "for" loop is called, the text change is ignored until AFTER the loop completes...
- (IBAction)start {
self.showLabel.text = [NSString stringWithFormat:#"5"];
for (NSString *p in self.currentNumber) {
NSLog(#"%#", p);
sleep(3);
}
}
#end
What am I missing?
(If you want to see the source files, you can get them at https://github.com/lamarrg/iterate
As you've realized, the UI will only update when the main thread is processing events. In a loop, it won't be.
There's a couple ways around this.
The simplest is to perform your loop in a background thread. There's a wrinkle, though: This will allow the user to continue to interact with your UI. And also, the UI can only be updated from the main thread.
You'll want to dispatch your work to the background, then have the background dispatch your work back to the main thread.
This sounds complicated, and it is. Thankfully, Apple added blocks and Grand Central Dispatch to Objective-C. You can use those to break down the chunks of code and make sure they're executed on the correct thread.
- (IBAction)start {
[self disableMyUI];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_NORMAL, 0), ^{
// this code will be executed "later", probably after start has returned.
// (in all cases, later should be considered "soon but not immediately.")
for (NSString *p in self.currentNumber) {
dispatch_async(dispatch_get_main_queue(),^{
// this code will be executed "later" by the main loop.
// You may have already moved on to the next thing, and even
// dispatched the next UI update.
// Don't worry; the main queue does things in order.
self.showLabel.text = [NSString stringWithFormat:#"%#", p];
});
sleep(3); // do your heavy lifting here, but keep in mind:
// you're on a background thread.
}
dispatch_async(dispatch_get_main_queue,^{
// this occurs "later," but after other all other UI events queued
// to the main queue.
[self enableMyUI];
});
}
// this line of code will run before work is complete
}
You'll have to write disableMyUI and enableMyUI; make sure they disable everything (including the back button if you're using navigation, the tab bar if you're using a tab bar controller, etc).
Another way around this is to use a NSTimer. However, if you do this you're still doing your work on the main thread. It'll work if you can split your work into predictable, small pieces, but you're better off doing it on a background thread.
One thing to keep in mind: Although you're not likely to run into problems while developing, doing heavy work on the main thread will lead to user crashes. On iOS there is a process that watches if applications are responding to events, such as drawing updates. If an application isn't responding to events in a timely fashion, it will be terminated. So living with the lack of UI updates isn't an option for you; you need to only do time consuming operations from background thread.
See also:
Programming with Objective-C: Working with Blocks
If you want to update the label periodically, don't use sleep. If you call it on the main thread you'll be blocking the UI, which is not very desirable.
Use a NSTimer instead, making it fire every N seconds.
Something like this will do:
- (void)startUpdatingLabel {
[NSTimer scheduledTimerWithTimeInterval:0 target:self selector:#selector(updateLabelWithIndex:) userInfo:#0 repeats:NO];
}
- (void)updateLabel:(NSTimer *)timer {
NSInteger index = [timer.userInfo integerValue];
if (index >= self.currentNumber.count) {
return;
}
self.showLabel.text = [NSString stringWithFormat:#"%#", self.currentNumber[index]];
[NSTimer scheduledTimerWithTimeInterval:3 target:self selector:#selector(updateLabelWithIndex:) userInfo:#(index+1) repeats:NO];
}
Every time updateLabel: is invoked it schedules a new timer which will call it again in 3 seconds. Each time the index value is increased and passed along.
I don't want to create NSTimer object. How do I invalidate timer? I want to invalidate timer in viewWillDisappear.
-(void) viewDidLoad
{
[super viewDidLoad];
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
}
A
you have to hold on to the timer you create:
#interface MONObject ()
#property (nonatomic, retain) NSTimer * timerIvar;
#end
#implementation MONObject
...
- (void)viewDidLoad
{
[super viewDidLoad];
self.timerIvar = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(onTimer:) userInfo:nil repeats:YES];
}
- (void)invalidateTimer
{
[self.timerIvar invalidate];
self.timerIvar = nil;
}
- (void)viewWillDisappear:(BOOL)animated
{
...
[self invalidateTimer];
}
B
another option would be to invalidate the timer that is passed in the callback, but that won't occur within viewDidUnload:. therefore, it doesn't quite apply in this scenario:
- (void)onTimer:(NSTimer *)pTimer
{
[pTimer invalidate];
}
If you want to be able to cancel the timer, you have to refer to the timer you’re cancelling, and that means you have to keep the pointer to the timer around, see justin’s answer.
Keeping a reference to the timer is the right way to do it, but for the sake of completeness you may also use the -performSelector:withObject:afterDelay: method as a poor man’s timer. That call may be invalidated using +cancelPreviousPerformRequestsWithTarget:. Sample code:
- (void) viewDidLoad
{
[super viewDidLoad];
[self performSelector:#selector(timerTick) withObject:nil afterDelay:10];
}
And then:
- (void) viewWillDisappear
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[super viewWillDisappear];
}
But this is not the right way to do it, because there might be other perform-selector requests pending on your object that you would cancel. It’s best to keep your timer around, that way you know exactly what you’re cancelling.
By the way, it’s also probably a bad idea to run a timer in -viewDidLoad. View loading may happen anytime, without any relation to view being displayed.
Maybe this method can help you:
[self performSelector:#selector(onTimer:) withObject:nil afterDelay:10];
If you don't want to hold on to your timer, the NSTimer object will be passed to the timer method (in your case onTimer:), so in that method you could check whether the timer is still needed and invalidate it. However, you will run into trouble if the view comes back before you invalidated the timer, and you create a new one.
By far the best way is to store the timer into an instance variable. It works, no clever tricks, and you'll know six months later what you did. I'd probably write a
#property (readwrite, nonatomic) BOOL hasTimer;
getter returns YES iff the timer is not nil, setter invalidates the timer or creates a new one.
This problem has been confusing me for days now. I have an NSString, 'spriteType'. It is declared using the property and synthesise method. In my layer, where the string is created, I also create a CCNode. The node is created in a method.
- (void) spritePick {
CCMenuItemImage *go = [CCMenuItemImage itemFromNormalImage:#"button_go.png" selectedImage:#"button_go_selected.png" target:self selector:#selector(test)];
spritePickMenu = [CCMenu menuWithItems:go, nil];
spritePickMenu.position = ccp(0,0);
spritePick = [CCNode node];
[spritePick addChild:spritePickMenu];
spritePick.position = ccp(240,160);
[self addChild: spritePick];
}
The 'test' method, which is called from the button, is simple:
- (void) test {
NSLog(#"%#",spriteType);
}
The NSLog line crashes my game, and gives the error: EXC_BAD_ACCESS
Anywhere apart from inside the 'test' method, the code works fine. Why would it be giving me the error when the method has been called from the Node, but it would not give it to me when it has been called from anywhere else?
I can give you the full code if required.
Which attributes are you using for your spriteType property declaration?
If your project is using ARC, the header should look like:
#property (nonatomic, strong) NSString *spriteType;
While the implementation should look like:
#synthesize spriteType = spriteType_;
If you declare/synthesize your property this way, then NSLog(#"%#", self.spriteType) just writes (null) to the console (I tested to double-check).
It's a good practice to access your properties using self. rather than trying to access the backing ivar directly.
Since you're seeing EXC_BAD_ACCESS I assume you aren't maintaining a strong reference to self.spriteType.
If your project is using ARC, you need to be sure you're compiling with LLVM 3.0 or greater, as detailed in this answer.