Objective-C: How to stop a NSTimer? - xcode

how would i go about stopping my timer whenever I try to do [myTimer invalidate]; I get an error.
My Timer:
NSTimer *MyTimer;
MyTimer = [NSTimer scheduledTimerWithTimeInterval: 1.0
target: self
selector: #selector(handleTimer)
userInfo: nil
repeats: YES];
Here's the if statement I have set up for the timer.
-(void)handleTimer{
if (count >= 0) {
[secondsLeft setText:[NSString stringWithFormat:#"%ld",(long)count]]; //displays current value of count to UI
count--; //Incrementally decreases count
} else if(count <= 0) { //if the timer runs out then...
self.secondsLeft.text = #"Times's Up!";
[answerButtonOutlet setEnabled:NO];
self.secondsLeftToAnswer.text = #"Answer is 30";
} else if([self.secondsLeftToAnswer.text isEqual: #"Correct!"]) { //else if statement if user gets answer Correct.
[myTimer invalidate]; // error occurs here. "Unknown receiver 'myTimer': did you mean NSTimer?
}
}
How would I make it so that I don't get the error in the second else if statement? I'm brand new to coding so I'm sure its probably pretty obvious but I can't figure it out for the life of me. Thanks!

Since NSTimer *MyTimer;
[myTimer invalidate]; should be [MyTimer invalidate];

You're better off just using the timer argument that gets passed to the handler method. In other words, you need an extra colon in the selector to indicate that the handler takes an argument
selector: #selector(handleTimer:) // note the colon at the end
Then your handler looks like this
-(void)handleTimer:(NSTimer *)theTimer
{
...
else if([self.secondsLeftToAnswer.text isEqual: #"Correct!"]) {
[theTimer invalidate];
}
}

You have [myTimer invalidate], when you should really have:
[MyTimer invalidate];
MyTimer = nil;
Try this change and let me know what happens. Also, throw an NSLog in that else if to make sure it's firing.

Related

Wait for asynchronous block to finish - signal (seems) do not work

I am using cryptotokenkit to send/receive data from smart card. The use case is, I must have the response from the card API before I do something else.
In my case I found that this line is always called after kMaxBlockingTimeSmartCardResponse (= 10) seconds.
From
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
it directly goes to
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kMaxBlockingTimeSmartCardResponse * NSEC_PER_SEC));
dispatch_semaphore_wait(sema, timeout);
Then wait for 10 seconds to come to the block call. Its not the block callback delaying. If I set the dispatch time 20 or 30 seconds, it wait for 20 or 30 seconds to execute. The wait call really wait for the specified time then callback block is executed.
What I am doing wrong? I wonder if this is related to adObserver.
- (void) updateSlots {
self.slotNames = [self.manager slotNames];
self.slotModels = [NSMutableArray new];
self.slots = [NSMutableArray new];
if([self.slotNames count] > 0)
{
for (NSString *slotName in self.slotNames)
{
NSLog(#"SmartCard reader found: %#", slotName);
// semaphore BLOCK starts
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self.manager getSlotWithName:slotName reply:^(TKSmartCardSlot *slot)
{
if (slot) {
SCSlotModel *slotModel = [SCSlotModel new];
[self.slotModels addObject:slotModel];
[self.slots addObject:slot];
[slot addObserver:self forKeyPath:#"state"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionInitial
context:nil];
slotModel.suffixedName = slotName; // NOT slot.name; original name is suffixed with 01, 02 etc.
slotModel.slot = slot;
slotModel.cardStatus = [CardUtil mapCardStatus:slot.state];
DLog(#"slot: %#, slotmodel: %#",slot.name, slotModel);
} else {
NSLog(#"Did not find slot with name: %#", slotName);
}
dispatch_semaphore_signal(sema);
}];
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kMaxBlockingTimeSmartCardResponse * NSEC_PER_SEC));
dispatch_semaphore_wait(sema, timeout);
// semaphore BLOCK ends
self.errorCode = SCPNoError;
}
} else {
NSLog(#"No slot available.");
self.errorCode = SCPErrorReaderNotFound;
}
}
getSlotWithName is the method from TKSmartCardSlotManager
/// Instantiates smartcard reader slot of specified name. If specified name is not registered, returns nil.
- (void)getSlotWithName:(NSString *)name reply:(void(^)(TKSmartCardSlot *__nullable slot))reply;
But in other places it works as expected, for the same type of asynchronous calls.
- (BOOL) beginSession:(TKSmartCard *)card
{
__block BOOL response = NO;
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[card beginSessionWithReply:^(BOOL success, NSError *error) {
response = success;
if (!success) {
NSLog(#"Could not begin session.");
}
if (error) {
NSLog(#"Could not begin session with error %#", error);
}
dispatch_semaphore_signal(sema);
}];
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kMaxBlockingTimeSmartCardResponse * NSEC_PER_SEC));
dispatch_semaphore_wait(sema, timeout);
return response;
}
What thread does getSlotWithName:reply: call its reply handler back on?
If for example updateSlots is executed on the main thread and you are doing something like this:
... // some async operation in `getSlotWithName:reply:` has completed
dispatch_async(dispatch_get_main_queue(), ^{
reply(slot)
});
then you would have ended up queueing your reply on the main thread however the main thread is currently locked with your semaphore.
Ensuring you call dispatch_semaphore_signal() from a thread other than the one you have locked should fix your issue.
Side Note: GCD and semaphores do the trick here but there are better ways you could perform actions after an async operation completes rather than letting a thread get locked up.. For example, delegate callbacks.
Not got enough information here to suggest anything exact but there are other options out there :)

Why doesn't this function get called?

Im following the Ray Wenderlich tutorial and converted most of the code to Swift. Im on part two but this function never gets called. I followed the tutoral but im not sure where to call it. I dont think its a delegate function also so I know that im supposed to call it somewhere but not sure where. Has anyone ever tried this tutorial and got it to work on Swift? Thanks!
Heres the link: http://www.raywenderlich.com/60998/game-center-tutorial-how-to-make-a-simple-multiplayer-game-with-sprite-kit-part-2
func match(theMatch: GKMatch, didReceiveData data: NSData, fromPlayer playerID: String) {
let message = UnsafePointer<Message>(data.bytes).memory
if(message.messageType == MessageType.kMessageTypeRandomNumber) {
let messageRandomNumber = UnsafePointer<MessageRandomNumber>(data.bytes).memory
println("Received random number: \(messageRandomNumber.randomNumber)")
var tie = false
if(messageRandomNumber.randomNumber == _ourRandomNumber) {
println("Tie")
tie = true
_ourRandomNumber = arc4random()
self.sendRandomNumber()
}
else {
var dictionary = ["\(playerIDKey)":"\(playerID)", "\(randomNumberKey)":"\(messageRandomNumber.randomNumber)"]
self.processReceivedRandomNumber(dictionary)
}
if(_receivedAllRandomNumbers) {
_isPlayer1 = self.isLocalPlayerPlayer1()
}
if(!tie && _receivedAllRandomNumbers) {
if(_gameState == GameState.kGameStateWaitingForRandomNumber) {
_gameState = GameState.kGameStateWaitingForStart
}
self.tryStartGame()
}
}
else if(message.messageType == MessageType.kMessageTypeGameBegin) {
println("Begin game message received")
_gameState = GameState.kGameStateActive
self.delegate?.setCurrentPlayerIndex(self.indexForLocalPlayer())
}
else if(message.messageType == MessageType.kMessageTypeMove) {
println("Move message received")
let messageMove = UnsafePointer<MessageMove>(data.bytes).memory
self.delegate?.movePlayerAtIndex(self.indexForPlayerWithId(playerIDKey))
}
else if(message.messageType == MessageType.kMessageTypeGameOver) {
println("Game over message received")
let messageGameOver = UnsafePointer<MessageGameOver>(data.bytes).memory
self.delegate?.gameOver(messageGameOver.player1Won)
}
}
The delegate method match:didReceiveData:fromPlayer: has been deprecated. Use match:didReceiveData:fromRemotePlayer: instead.
Make sure you set the delegate, doing something like:
func matchmakerViewController(viewController: GKMatchmakerViewController, didFindMatch match: GKMatch) {
theMatch = match
match.delegate = self
...
}
That is the viewController:didFindMatch delegate for GKMatchmakerViewControllerDelegate.
You need to delegate from both GKMatchmakerViewControllerDelegate and GKMatchDelegate.
Yeah, I know this is an old thread, but the OP hasn't had any success yet.
match:didReceiveData:fromPlayer: is a GKMatchDelegate optional method which will trigger when the match received data sent from the player.
So please don't try to call it directly. It will be called by GameKit when data is received from a player.
#protocol GKMatchDelegate <NSObject>
#optional
// The match received data sent from the player.
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromRemotePlayer:(GKPlayer *)player NS_AVAILABLE(10_10, 8_0);
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID NS_DEPRECATED(10_8, 10_10, 4_1, 8_0, "use match:didReceiveData:fromRemotePlayer:");
#end;
This also is declared in GameKitHelper.h class as a GameKitHelperDelegate
#protocol GameKitHelperDelegate
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data
fromPlayer:(NSString *)playerID;
#end
This delegate method is calling from the below method when the match received data sent from the player
#pragma mark GKMatchDelegate
// The match received data sent from the player.
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
if (_match != match) return;
[_delegate match:match didReceiveData:data fromPlayer:playerID];
}

How to wait [NSWorkspace recycleURLs] completion synchronously

I need to move files on trash using NSWorkspace recycleURLs but I must wait completion, putting all code in the completionHandler isn't so simple, mainly because the method containing the call to recycleURLs must return a value.
I've found a dirty way to achieve the result but I would know if exists a better solution.
My solution uses a conditional variable and runloop
The code runs in a separated thread, not in main thread
__block BOOL waitCompletion = YES;
void (^myCompletionHandler)(NSDictionary *newURLs, NSError *error) =
^(NSDictionary *newURLs, NSError *recycleError) {
// do some stuff and before exits from method change waitCompletion value
waitCompletion = NO;
};
[[NSWorkspace sharedWorkspace] recycleURLs:myURLToDelete
completionHandler:myCompletionHandler];
// loop until completionHandler finishes
while (waitCompletion && CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2.0, true)) {
; //nop
}
// we can continue after recycleURLs
...
...

using NSOperationQueue and blocks

I am having a little trouble using addoperationwithblock in Cocoa. Let's say I have a master function
-(IBAction) callthisone {
// Call another function "slave" here and store returned value in result
result = return value from slave
NSLog(#" result is %#",result);
}];
}
-(NSArray *) slave {
[operationQueue addOperationWithBlock: ^{
NSString * result = #"5" ;
}];
return result;
}
I can never get the result value returned in the master. How do I do this ? Is my approach correct ? Thanks
You may try something like this:
-(IBAction) callthisone {
[self slave: ^(NSString* result) {
NSLog(#" result is %#",result);
}
];
}
-(void)slave: (void(^)(NSString*)) callback {
[operationQueue addOperationWithBlock: ^{
NSString* str = [NSString stringWithFormat: #"5]";
callback(str);
}
];
}
Apple's documentation for addOperationWithBlock says:
Parameters
The block to execute from the operation object. The block should take
no parameters and have no return value.
These are meant for self contained block operations.
Could you try something different that does have more flexibility in terms of getting stuff in and out of the queue / thread? Maybe Grand Central Dispatch (I was just looking at this thread).

Notification when the system is idle or not on OS X

I know that there is some way to get the system idle time with the IOKit framework on OS X, but I want to know if there's notifications available.
I can create a timer to check if the idle time is more than x, and that's fine. It doesn't matter if I detect the idle mode a few seconds later.
The problem is to detect when the Mac is not idle anymore. I want my app to show a notification as soon as possible, not a few seconds later.
Is there a way to have a notification for that? (iChat seems to have one)
This is from http://developer.apple.com/library/mac/#qa/qa1340/_index.html (from Bill the Lizard comment)
- (void) receiveSleepNote: (NSNotification*) note
{
NSLog(#"receiveSleepNote: %#", [note name]);
}
- (void) receiveWakeNote: (NSNotification*) note
{
NSLog(#"receiveWakeNote: %#", [note name]);
}
- (void) fileNotifications
{
//These notifications are filed on NSWorkspace's notification center, not the default
// notification center. You will not receive sleep/wake notifications if you file
//with the default notification center.
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: #selector(receiveSleepNote:)
name: NSWorkspaceWillSleepNotification object: NULL];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver: self
selector: #selector(receiveWakeNote:)
name: NSWorkspaceDidWakeNotification object: NULL];
}
NSTimeInterval GetIdleTimeInterval() {
io_iterator_t iter = 0;
int64_t nanoseconds = 0;
if (IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHIDSystem"), &iter) == KERN_SUCCESS) {
io_registry_entry_t entry = IOIteratorNext(iter);
if (entry) {
CFMutableDictionaryRef dict;
if (IORegistryEntryCreateCFProperties(entry, &dict, kCFAllocatorDefault, 0) == KERN_SUCCESS) {
CFNumberRef obj = CFDictionaryGetValue(dict, CFSTR("HIDIdleTime"));
if (obj)
CFNumberGetValue(obj, kCFNumberSInt64Type, &nanoseconds);
CFRelease(dict);
}
IOObjectRelease(entry);
}
IOObjectRelease(iter);
}
return (double)nanoseconds / 1000000000.0;
}

Resources