Stop pinwheel of death in infinite loop Objective C - objective-c

I am writing a simple timer program for myself in objective c for my mac. The timer counts down properly, but I get the spinning pinwheel of death. How can I make the pinwheel go away? I think its because I have an infinite loop but there must be someway to bypass it.
I have an IBAction that triggered on a button click (the button is start). And from there, it calls another function that does the work.
Here is my IBAction:
- (IBAction)timerStart:(id)sender {
self.timerDidPause = NO;
[self timerRunning];
}
And here is timerRunning:
- (void)timerRunning {
for (;;) {
usleep(1000000);
if (self.timerDidPause == YES) {
}
else {
if (self.seconds == 0) {
if (self.minutes == 0) {
[self timerDone];
break;
}
else {
self.seconds = 59;
self.minutes = self.minutes - 1;
[self formatTimerLabel:self.hours :self.minutes :self.seconds];
}
}
else {
self.seconds = self.seconds - 1;
[self formatTimerLabel:self.hours :self.minutes :self.seconds];
}
}
}
}
In this function, the function formatTimerLabel is called so here is that:
- (void)formatTimerLabel:(int)hours
:(int)minutes
:(int)seconds {
NSString *minuteString = [[NSString alloc] init];
NSString *secondString = [[NSString alloc] init];
if (minutes < 10) {
minuteString = [NSString stringWithFormat:#"0%d", minutes];
}
else {
minuteString = [NSString stringWithFormat:#"%d", minutes];
}
if (seconds < 10) {
secondString = [NSString stringWithFormat:#"0%d", seconds];
}
else {
secondString = [NSString stringWithFormat:#"%d", seconds];
}
[self.timerLabel setStringValue:[NSString stringWithFormat:#"%d:%#:%#", hours, minuteString, secondString]];
[self.timerLabel display];
}

You're causing the UI thread to hang with your loop. After a couple of seconds of that, the OS switches the cursor to a pinwheel.
You need to look into NSTimer and the Timer Programming Guide to schedule the timer to run outside of the UI thread.

Related

How to implement a while loop with timeout?

I want to implement a while loop that exits either when a particular condition is met, or when a timer times out.
If I just start the timer (to set an object variable on timeout), and then start the while loop (checking the object variable), it doesn't work, because the timer never times out.
I've tried 3 of the solutions suggested in How to wait for a thread to finish in Objective-C to make the loop run in a separate function on another thread, but they fail in various different ways. I have not yet managed to get a test run where the timer times out.
The simple implementation was
//object variable
BOOL m_readTimedOut;
- (void) someFunction
{
m_readTimedOut = NO;
float timeoutS = 0.1;
//Start the timer
SEL readTimeoutSelector = sel_registerName("onReadTimeout:");
[NSTimer scheduledTimerWithTimeInterval:timeoutS
target:self
selector:readTimeoutSelector
userInfo:nil
repeats:NO];
int numBytesToRead = 1;
BOOL exit = NO;
int counter = 0;
while ((counter < numBytesToRead) && (exit == NO))
{
#try
{
#synchronized (readBufferLock)
{
//m_readBuffer is an object variable that can be altered on another thread
if ([m_readBuffer length] > 0)
{
//Do stuff here
counter++;
}
} //end synchronised
}
#catch (NSException *exception)
{
//Log exception
}
if (counter == numBytesToRead || m_readTimedOut == YES)
{
exit = YES;
}
} //end while
}
- (void)onReadTimeout:(NSTimer *)timer
{
NSLog(#"Read timer timed out");
m_readTimedOut = YES;
}
Just a try on the timed exit only - how about
NSDate * start = [[NSDate alloc] init]; // When we started
while ( counter < something )
{
// do stuff ...
// Check time
NSDate * now = [[NSDate alloc] init];
// Been here more than 10s since start
if ( [now timeIntervalSinceDate:start] > 10 )
{
// Timed exit
break;
}
}

How to perform all other tasks once the asynchronous dispatch queue finishes execution

I have a PDFDocument where it has some actions on pages like delete,crop,rotate etc.
So when I click on the delete button and click on save(current thread : main thread)
-(void)save
{
// -- process deleted pages first
for( NSString* deletedPageId in self.deletedPageIdList )
{
[self.pdfCoordinator removePageWithId:deletedPageId];
}
// -- wait for all pages to delete before continuing
[self.pdfCoordinator waitUntilAllOperationsAreFinished];
// few lines of code after this should run only when the above code finishes its execution
// code to save the changes in the pdf to managedObject context
}
The code for removePageWithId:
- (void) removePageWithId:(NSString*)pageId
{
NRMPDFOperation *op = [[NRMPDFOperation alloc] initWithNRMPDF:self
pageId:pageId
selector:#selector(removePageOpImpl:)
args:nil];
[self addOperation:op];
[op release];
}
above code creates an operation and adds it to the operation queue for each deletion of the page
code for removePageOpImpl:
- (NSError *)removePageOpImpl:(NRMPDFOperation *)op
{
NSError* error = [self loadDocument];
if( !error )
{
NSUInteger index = [self pageIndexForId:[op pageId]];
if( index < [self pageCount] )
{
[[self pdfDocument] removePageAtIndex:index];
[[self mutablePageIdList] removeObjectAtIndex:index];
[self updatePageLabelsFromIndex:index];
[self updateChangeCount:NSChangeDone];
self.contentsChanged = YES;
}
else
{
// TODO: error
}
}
return error;
}
In the removePageOpImpl: method the line of code
[[self pdfDocument] removePageAtIndex:index]; internally executing some tasks on main thread(but we are making the main thread to wait until this operation finishes).which causes the deadlock.
I tried to execute the code inside removePageOpImpl: in an asynchronous dispatch queue to avoid the deadlock.below is the code for that
- (NSError *)removePageOpImpl:(NRMPDFOperation *)op
{
NSError* error = [self loadDocument];
if( !error )
{
NSUInteger index = [self pageIndexForId:[op pageId]];
if( index < [self pageCount] )
{
dispatch_async(dispatch_get_main_queue(), ^{
[[self pdfDocument] removePageAtIndex:index];
[[self mutablePageIdList] removeObjectAtIndex:index];
[self updatePageLabelsFromIndex:index];
[self updateChangeCount:NSChangeDone];
self.contentsChanged = YES;
});
}
else
{
// TODO: error
}
}
return error;
}
Now I am out from the deadlock. But another issue is by putting the above code into asynchronous block the code which should run after this tasks is executing before this, because of that my app is not behaving as expected.
Code inside waitUntilAllOperationsAreFinished method
- (void) addOperation:(NRMPDFOperation*)operation
{
[_operationSet addObject:operation];
[[self operationQueue] addOperation:operation];
}
- (void) waitUntilAllOperationsAreFinished
{
[[self operationQueue] waitUntilAllOperationsAreFinished];
}
- (NSOperationQueue *)operationQueue
{
if( !_operationQueue )
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
_operationQueue = queue;
}
return _operationQueue;
}
This is how the original Save method looks like :
- (void)saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo
{
// TODO: don't just ignore saveOperation
__block BOOL success = YES;
__block NSError *error = nil;
/* write back field changes */
if ([item hasChangesInEditFieldsetFor:#"values"] )
{
//Some code
}
if( self.isPDFEdited )
{
// -- process deleted pages first
for( NSString* deletedPageId in self.deletedPageIdList )
{
[self.itemPDFCoordinator removePageWithId:deletedPageId];
}
// -- wait for all pages to delete before continuing
[self.itemPDFCoordinator waitUntilAllOperationsAreFinished];
// -- replace the search text any pages we deleted
if( [self.deletedPageIdList count] )
{
[self.item setValue:[self.editPDF string] forKeyPath:#"dict.values._searchText"];
}
NSMutableDictionary* originalRotations = [NSMutableDictionary dictionaryWithCapacity:
[self.itemPDFCoordinator pageCount]];
for( NSString* pageId in self.itemPDFCoordinator.pageIdList )
{
NSInteger rotation = [[self.itemPDFCoordinator pageForId:pageId] rotation];
[originalRotations setObject:[NSNumber numberWithInteger:rotation] forKey:pageId];
}
// -- now process actions on remaining pages (crop, rotate, and convert to b&w)
BOOL didCropAnyPages = NO;
NSMutableArray* convertToBwJobs = [NSMutableArray array];
for( NSString* pageId in [self.pageActionDict allKeys] )
{
NSArray* actions = [self.pageActionDict objectForKey:pageId];
for( NSDictionary* action in actions )
{
NSNumber* rotationNumber = [action objectForKey:#"rotation"];
NSValue* cropRectVal = [action objectForKey:#"cropRect"];
NSNumber* convertToBlackAndWhite = [action objectForKey:#"convertToBlackAndWhite"];
if( rotationNumber )
{
[self.itemPDFCoordinator rotateByDegrees:[rotationNumber integerValue]
forPageWithID:pageId];
}
else if( cropRectVal )
{
[self.itemPDFCoordinator setNormalizedBounds:[cropRectVal rectValue]
forBox:kPDFDisplayBoxCropBox
forPageWithID:pageId];
// -- set a flag so we know to recrop the entire document
didCropAnyPages = YES;
}
else if( [convertToBlackAndWhite boolValue] )
{
NSUInteger pageIndex = [self.itemPDFCoordinator pageIndexForId:pageId];
NRMJob* job = [NRMAppJobFactory convertToBlackAndWhiteJobForItem:self.item
pageIndex:pageIndex];
[convertToBwJobs addObject:job];
}
}
}
// -- reapply crop box to any cropped pages
if( didCropAnyPages )
{
[self.itemPDFCoordinator applyCropBoxToAllPages];
}
[self.itemPDFCoordinator waitUntilAllOperationsAreFinished];
for( NRMJob* job in convertToBwJobs )
{
if( ![[self.masterDocument docjob] addJob:job forItem:self.item error:&error] )
[NSApp presentError:error];
else
[job waitUntilDone];
}
// -- make sure document attributes are updated
NSDictionary *docDict = [self.itemPDFCoordinator documentAttributes];
NSDictionary *newDict = [(NRMItem *)item updateDocumentAttributes:docDict];
if (![newDict isEqualToDictionary:docDict])
[self.itemPDFCoordinator setDocumentAttributes:newDict];
[self.itemPDFCoordinator waitUntilAllOperationsAreFinished];
// -- check if we need to reprocess any pages
for( NSString* pageId in self.itemPDFCoordinator.pageIdList )
{
NSInteger oldRotation = [[originalRotations objectForKey:pageId] integerValue];
NSInteger newRotation = [[self.itemPDFCoordinator pageForId:pageId] rotation];
if( oldRotation != newRotation )
{
// -- if it's an image page and we already have OCR data for it, we should reanalyze
NSUInteger pageIndex = [self.itemPDFCoordinator pageIndexForId:pageId];
BOOL isPageImage = [self.itemPDFCoordinator isImagePageAtIndex:pageIndex DPI:NULL];
if( isPageImage && [item OCRDataForPageIndex:pageIndex] )
{
NRMJob* job = [NRMAppJobFactory reprocessPageJobForItem:self.item
pageIndex:pageIndex];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reanalyzeJobFinished:)
name:kNRMJobFinishedNotification
object:job];
success = [[self.masterDocument docjob] addJob:job forItem:self.item error:&error];
if( !success )
{
if( error )
[self presentError:error];
}
//goto bail;
}
}
}
//Force Save of PDF to Disk
[self.itemPDFCoordinator setManagedObjectContext:[item managedObjectContext]];
[self.itemPDFCoordinator saveChanges];
}
if( success )
{
[self updateChangeCount:NSChangeCleared];
if( self.isPDFEdited )
{
// !CLEARSPLIT! please do not remove this comment
[self.item setValue:nil forKeyPath:#"dict.values._splitId"];
if( ![self loadPDFForItem:item error:&error] )
goto bail;
}
}
bail:
if( error )
[self presentError:error];
if( delegate )
{
/* signature:
- (void)document:(NSDocument *)document didSave:(BOOL)didSaveSuccessfully contextInfo:(void *)contextInfo;
*/
objc_msgSend( delegate, didSaveSelector, self, (error ? NO : YES), contextInfo );
}
}
can anyone suggest me how can I get out of this issue.
Rather than calling waitUntilAllOperationsAreFinished (which blocks a thread), I'd suggest specifying an additional completion operation, which you can make dependent upon the other operations finishing:
-(void)save {
NSOperation *completionOperation = [NSBlockOperation blockOperationWithBlock:^{
// whatever you want to do when it's done
}];
for (NSString* deletedPageId in self.deletedPageIdList) {
NSOperation *operation = [self.pdfCoordinator removePageWithId:deletedPageId];
[completionOperation addDependency:operation];
}
// add this completionOperation to a queue, but it won't actually start until the other operations finish
[queue addOperation:completionOperation];
}
Clearly this assumes that you change removePageWithId to return the operation it added to the queue:
- (NSOperation *)removePageWithId:(NSString*)pageId {
NRMPDFOperation *operation = [[NRMPDFOperation alloc] initWithNRMPDF:self
pageId:pageId
selector:#selector(removePageOpImpl:)
args:nil];
[self addOperation:operation];
[operation release];
return operation;
}
This way, you're not blocking any thread waiting for the operations to finish, but simply specify what to do when they do finish.

EXC_BAD_ACCESS Error for type NSString

I'm new to this mac application development.
The app is working fine for some data and the app crashes for few entries.
-(void)presentClientsss
{
[productVendorTextField setStringValue:[NSString stringWithFormat:#"%#", [[popUpVendor selectedItem] title]]];
NSMenuItem *tempMenuItem = [popUpVendor selectedItem];
NSString *selectedItemTitle = [tempMenuItem title];
for (int k = 0; k < [appDelegate.vendorInfoArr count]; k++)
{
VendorInfo *tempCustomerInfoModel = [appDelegate.vendorInfoArr objectAtIndex:k];
if ([tempCustomerInfoModel.vendorName isEqualToString:selectedItemTitle])
{
oldVendorIde = [NSString stringWithFormat:#"%ld", tempCustomerInfoModel.rowId];
NSLog(#"Selected RowID = %#",oldVendorIde);
break;
}
}
}
I'm sending the oldVendorIdestring to next method.
- (ItemModel *)itemNodelWithAttributes {
isProductIdExist = NO;
if ([senderInfo isEqualToString:#"nP"]) {
for (int i = 0; i < [appDelegate.itemsArr count]; i++) {
ItemModel *tempIM = [appDelegate.itemsArr objectAtIndex:i];
if ([tempIM.productId isEqualToString:[[productIdTextField stringValue] uppercaseString]]) {
isProductIdExist = YES;
break;
}
}
}
if ([senderInfo isEqualToString:#"eP"]) {
for (int i = 0; i < [appDelegate.itemsArr count]; i++) {
ItemModel *tempIM = [appDelegate.itemsArr objectAtIndex:i];
if (tempIM.itemId == itemIdentity) {
if ([tempIM.productId isEqualToString:[[productIdTextField stringValue] uppercaseString]]) {
isProductIdExist = NO;
}
}
else if ([tempIM.productId isEqualToString:[[productIdTextField stringValue] uppercaseString]]) {
isProductIdExist = YES;
}
}
}
int tempItemExists = [self saveProductImage:[[productIdTextField stringValue] uppercaseString]];
NSLog(#"oldVendorIde =%#",oldVendorIde);
ItemModel *iM = [[ItemModel alloc] initWithItemId:itemIdentity defaultItemMinimumValue:[productMinValueTextField floatValue] staticItemPrice:[productPriceTextField doubleValue] dynamicItemQuantity:[productCurrentStockTextField doubleValue] staticItemDescription:[productDescriptionTextField stringValue] prodId:[[productIdTextField stringValue] uppercaseString] itemVendor:oldVendorIde itemImgExists:tempItemExists stockAvailable:0 itemNotes:[notesTextField string] BarcodeDesc:[BarcodeDescTextView stringValue]];
return iM;
}
In this method the same oldVendorIde is working fine for some data and some time it gets crashed at this point.
The oldVendorIde sometime doesnot get any value in itemNodelWithAttributes method and the app crashes at that point.
Can Sone help me to solve the issue.. Thanks in advance..
The text from a UITextField is accessed through the text property ([productIdTextField text]), not through stringValue.

Using NSTimer with two intervals

I'm creating an app that converts text to Morse code, and then flash it out using the iPhone's flashlight. I have used string replacement, to convert the content of a NSString to Morse code.
// some of the code :
str = [str stringByReplacingOccurrencesOfString:#"5" withString:n5];
str = [str stringByReplacingOccurrencesOfString:#"6" withString:n6];
str = [str stringByReplacingOccurrencesOfString:#"7" withString:n7];
str = [str stringByReplacingOccurrencesOfString:#"8" withString:n8];
str = [str stringByReplacingOccurrencesOfString:#"9" withString:n9];
str = [str stringByReplacingOccurrencesOfString:#"0" withString:n0];
NSString *morseCode = [[NSString alloc] initWithFormat:str];
self.label.text = morseCode;
I have found a script that turns the iPhone's flashlight on and off, with adjustable intervals using NSTimer. But I can't figure out how to add two different intervals, one for the dot and one for the Morse dash.
- (void)viewDidLoad
{
[super viewDidLoad];
int spaceTime;
spaceTime = 1;
int dashTime;
dashTime = 2;
int dotTime;
dotTime = 0.8;
strobeIsOn = NO;
strobeActivated = NO;
strobeFlashOn = NO;
flashController = [[FlashController alloc] init];
self.strobeTimer = [
NSTimer
scheduledTimerWithTimeInterval:spaceTime
target:self
selector:#selector(strobeTimerCallback:)
userInfo:nil
repeats:YES
];
self.strobeFlashTimer = [
NSTimer scheduledTimerWithTimeInterval:dotTime
target:self
selector:#selector(strobeFlashTimerCallback:)
userInfo:nil
repeats:YES
];
}
- (void)strobeTimerCallback:(id)sender {
if (strobeActivated) {
strobeIsOn = !strobeIsOn;
// ensure that it returns a callback. If no, returns only one flash
strobeFlashOn = YES;
} else {
strobeFlashOn = NO;
}
}
- (void)strobeFlashTimerCallback:(id)sender {
if (strobeFlashOn) {
strobeFlashOn = !strobeFlashOn;
[self startStopStrobe:strobeIsOn];
} else {
[self startStopStrobe:NO];
}
}
Should I use two timers or can I have one with different intervals? Should I put the content of the string in an array?
I'm new in Obj-C ..
I would try to make a recursive function:
parseAndFlash
{
NSString *codeString = #"-.-. --- -.. .";
int currentLetterIndex = 0;
//codeString and currentLetterIndex should be declared outside this function as members or something
double t_space = 2, t_point = 0.5, t_line = 1, t_separator = 0.1;
double symbolDuration = 0;
if(currentLetterIndex >= [codeString length])
return;
char currentLetter = [codeString characterAtIndex:currentLetterIndex];
switch (currentLetter) {
case '-':
symbolDuration = t_line;
[self flashOnFor:t_line];
break;
case '.':
symbolDuration = t_point;
[self flashOnFor:t_point];
break;
case ' ':
symbolDuration = t_space;
[self flashOff];
break;
default:
break;
}
currentLetterIndex ++;
symbolDuration += t_separator;
[self performSelector:#selector(parseAndFlash) withObject:nil afterDelay:symbolDuration];
}
you can try to run code in sequence on background treed and sleep it for as long as you need. It would be much easier code to write and maintain than to use bunch of timers.
// execute in background
[self performSelectorInBackground:#selector(doTheMagic) withObject:nil];
- (void)doTheMagic {
NSLog(#"Turn ON");
[NSThread sleepForTimeInterval:1];
NSLog(#"Turn OFF");
[NSThread sleepForTimeInterval:0.1f];
NSLog(#"Turn ON");
[NSThread sleepForTimeInterval:1.0f];
// ...
}

execution stops on [textView insertText:] without exception!

I am writing a very simple OSX app. In this app, there is a main thread that keeps updating some stuff, then calculates some statistics and prints them on a textView.
While developing, i used the same received IBAction to perform this cycle. I got it all working, then switched to NSThread to prevent the UI from locking while computing.
As soon as i did that, the app started running very few cycles (about 7-8), then the whole app freezes without any exception. By debugging, i noticed that it freezes when trying to print statistics on the textView, and i have absolutely no clue about how to solve this. It works if not inside a thread...
Anyone can help? Code below. Thanks in advance :)
-(IBAction) Evolve:(id)sender{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector:#selector(Evolve) toTarget:self withObject:nil];
[pool drain];
}
And this is the whole cycle
-(void) Evolve{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
srandom(time(NULL));
//Tag 0: Minimo, Tag 1: Massimo
int tagMinMax = [MinMax selectedTag];
//Tag 0: Rimpiazza sempre, Tag 1: Rimpiazza solo se migliori
int tagRimpiazzo = [[Rimpiazzo selectedItem] tag];
int PopNum = [tf_popNum intValue];
int maxGen = [tf_maxGen intValue];
int target = [tf_targetVal intValue];
int chromosomeLength = [tf_chromosomeLength intValue];
Environment *env = [[Environment alloc] init];
NSMutableArray *pop = [[NSMutableArray alloc] init];
for (int i = 0; i < PopNum; i++) {
[pop addObject:[[Individual alloc] initWithRandomGenesWithChromosomeLength:chromosomeLength]];
}
[env setPopulation:pop];
[pop release];
BOOL earlyBestFound = NO;
Individual *earlyBest = nil;
int i=0;
float best, avg;
while (i<maxGen && !earlyBestFound) {
NSLog(#"while");
NSArray *parents = [env selectParents];
NSLog(#"parents selected");
NSMutableArray *offspring = [[NSMutableArray alloc] init];
for (int i = 0; i < [parents count]; i+=2) {
if (i+1<[parents count]) {
NSLog(#"beginning SEX");
Individual *parent1 = [parents objectAtIndex:i];
Individual *parent2 = [parents objectAtIndex:i+1];
NSArray *children = [parent1 kCrossOverWithOtherIndividual:1 individual:parent2];
Individual *child1 = [children objectAtIndex:0];
Individual *child2 = [children objectAtIndex:1];
NSLog(#"children born");
if (tagRimpiazzo!=0) {
if (([child1 fitness] > [parent1 fitness] && tagMinMax == 0)||([child1 fitness] < [parent1 fitness] && tagMinMax == 1)) {
child1 = parent1;
}
if (([child2 fitness] > [parent2 fitness] && tagMinMax == 0)||([child2 fitness] < [parent2 fitness] && tagMinMax == 1)) {
child2 = parent2;
}
}
NSLog(#"Replacement happened");
[offspring addObject:child1];
[offspring addObject:child2];
}
}
NSLog(#"Calculating statistics");
avg = 0;
for(Individual *X in offspring){
if (([X fitness] > best && tagMinMax == 1)||([X fitness] < best && tagMinMax == 0)) {
best = [X fitness];
}
avg += [X fitness];
if ([X fitness]==target) {
earlyBestFound = YES;
earlyBest = X;
}
}
avg = avg/(float)PopNum;
[env setPopulation:offspring];
NSLog(#"Releasing some memory");
[offspring release];
NSLog(#"Printing statistics");
NSString *toPrint = [NSString stringWithFormat:#"Fine generazione: %d avg: %.2f best: %.2f \r\n", i,avg,best];
[textView insertText:toPrint];
i++;
}
NSLog(#"Fine");
NSString *toPrint = [NSString stringWithFormat:#"Fine! best: %.2f - avg: %.2f \r\n", i,avg,best];
[textView insertText:toPrint];
[env release];
[pool drain];
}
P.S.
Sorry if the english's not perfect, i'm italian :)
Your application is crashing because you are accessing textView from a background thread. UI objects may only be accessed from the main thread.
To solve the problem, you will need to forward you textview updates to the main UI thread. You can do this using the -performSelectorOnMainThread: method. For example:
[textView performSelectorOnMainThread:#selector(insertText:)
withObject:toPrint
waitUntilDone:YES];