iOS 10 while(YES) never break - objective-c

I have sample code below.
- (void)method1
{
breakFlag = NO;
[self performSelectorInBackground:#selector(method2) withObject:nil];
while (YES) {
if (breakFlag) {
break;
}
}
NSLog(#"End method1");
}
- (void)method2
{
for (int i = 0; i < 10000; i++) {
NSLog(#"value of i; %d", i);
}
breakFlag = YES;
NSLog(#"End method2");
}
I called method1 from viewDidAppear with
[self performSelectorInBackground:#selector(method1) withObject:nil];
breakFlag is variable instance.
Why while in method1 never be broken?

C and Objective-C compilers are not really aware of threading. They are, according to the language standard, perfectly entitled to examine code like your -method1 and see that nothing in the code of the loop changes breakFlag and optimize it by hoisting the check out of the loop. That is, it optimizes your method into:
- (void)method1
{
breakFlag = NO;
[self performSelectorInBackground:#selector(method2) withObject:nil];
if (!breakFlag) {
while (YES) {
}
}
NSLog(#"End method1");
}
It does need to check breakFlag after the call to -performSelectorInBackground:... because it can't know that that won't change breakFlag. But it doesn't need to check it within the loop.
If you were to declare breakFlag with the volatile qualifier, that would prevent this optimization. That tells the compiler that something can "spontaneously" (outside of what it can analyze) modify breakFlag at any time.
However, that's almost never the correct way to write code. Since you've refused to say what you're trying to accomplish, it's hard to advise you on better ways to achieve it. A general recommendation is to suggest that you use proper thread synchronization primitives, like semaphores, mutexes, etc. to have threads interact.

Instead of:
[self performSelectorInBackground:#selector(method2) withObject:nil];
Try:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self method2];
});

Related

Draw the contents of an array modified in the background

I have a background thread that runs and modifies the contents of a NSMutableArray within an object. This takes a long time to run (several hours) and I periodically want to draw the contents of an array within the drawRect of a NSView to check on progress and see the intermediate results.
My object has a protocol with a method called: didChange:
// How I start my background thread
[self performSelectorInBackground:#selector(startProcessing) withObject:nil];
- (void)startProcessing {
myObject.delegate = self;
[myObject start];
}
// My protocol implementation
- (void)myObjectDidChange:(myObjectClass *)sender {
[myView setNeedsDisplay:YES];
}
// My View's drawRect (pseudo code)
- (void)drawRect {
[myObject drawInContext:context];
}
All works, except that the NSMutableArray backing all this is being changed whilst the drawing takes place. How should I do this? Do I somehow pause the processing in the background thread whilst the update is taking place?
EDIT: This is the sort of display I am drawing (although much more complicated):
Any help appreciated.
If you are doing something in background thread and you want to update UI, its usually done on the main thread, so in your object did change you would do it, probably like this:
// My protocol implementation
- (void)myObjectDidChange:(myObjectClass *)sender {
dispatch_async(dispatch_get_main_queue(), ^{
[self drawRect]; //Or any drawing function you are trying to do
});
}
I have done it using NSLock to lock the outer loop of the start and the drawInContext methods. I am still not sure if this is the best approach and will not accept this answer for a few days in case there is a better answer out there.
- (void)start {
for(int i=0; i < MAX; i++) {
[self.updateLock lock];
....
[self.updateLock unlock];
}
}
- (void)drawInContext:(CGContextRef)context {
[self.updateLock lock];
...
[self.updateLock unlock];
}

NSThread Not Loading Selector Method

In the initialization method of a class I am declaring the thread as such:
NSThread* myThread = [[[NSThread alloc] initWithTarget:self selector:#selector(m_run_thread) object:nil] autorelease];
[myThread start];
I also have a boolean value which is set to NO. Later on in the code I set the boolean value to YES.
bool_run_progress_thread = YES;
The contents of the method m_run_thread is as follows:
-(void) m_run_thread
{
if (bool_run_progress_thread)
{
//do processing here
}
bool_run_progress_thread = NO;
}
The problem is that the method m_run_thread is never being accessed. What am I doing wrong?
P.S. I have also tried to set up the Thread using the following (and older)method:
[NSThread detachNewThreadSelector:#selector(m_run_thread)
toTarget:self
withObject:nil];
... but to no avail as well.
"...and I am only getting it to show once" Yes, that's exactly how it should be. After being started, a thread runs once from its start to its end (ignoring errors here for the moment), and having reached the end, the thread is essentially dead and gone.
If you want the thread to repeat its execution, you have to prepare for that yourself:
- (void) m_run_thread
{
for (;;)
{
if (bool_run_progress_thread)
{
//do processing here
bool_run_progress_thread = NO;
}
}
}
But there is still a lot wrong with this code: essentially, when run, the code forms a busy waiting loop. Assuming, that bool_run_progress_thread is only ever true for short periods of time, the background thread should be sleeping most of the time. Insead, if you try the code as its stands, it will instead consume CPU time (and lots of it).
A better approach to this would involve condition variables:
#class Whatsoever
{
NSCondition* cvar;
BOOL doProgress;
...
}
...
#end
and
- (void) m_run_thread
{
for (;;)
{
[cvar lock];
while (!doProgress)
{
[cvar wait];
}
doProgress = NO;
[cvar unlock];
... do work here ...
}
}
and in order to trigger the execution, you'd do:
- (void) startProgress
{
[cvar lock];
doProgress = YES;
[cvar signal];
[cvar unlock];
}
Doing things this way also takes care of another subtle problem: the visibility of the changes made to the global flag (your bool_run_progress_thread, my doProgess). Depending on the processor and its memory order, changes made without special protection might or might not become (ever) visible to other threads. This problem is taken care of by the NSCondition, too.

Synchronizing a Block within a Block?

I'm playing around with blocks in Objective-C, trying to come up with a reusable mechanism that will take an arbitrary block of code and a lock object and then execute the block of code on a new thread, synchronized on the provided lock. The idea is to come up with a simple way to move all synchronization overhead/waiting off of the main thread so that an app's UI will always be responsive.
The code I've come up with is pretty straightforward, it goes like:
- (void) executeBlock: (void (^)(void))block {
block();
}
- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
void(^syncBlock)() = ^{
#synchronized(lock) {
block();
}
};
[self performSelectorInBackground:#selector(executeBlock:) withObject:syncBlock];
}
So for example, you might have some methods that go like:
- (void) addObjectToSharedArray:(id) theObj {
#synchronized(array) {
[array addObject: theObj];
}
}
- (void) removeObjectFromSharedArray:(id) theObj {
#synchronized(array) {
[array removeObject: theObj];
}
}
Which works fine, but blocks the calling thread while waiting for the lock. These could be rewritten as:
- (void) addObjectToSharedArray:(id) theObj {
[self runAsyncBlock:^{
[array addObject: theObj];
} withLock: array];
}
- (void) removeObjectFromSharedArray:(id) theObj {
[self runAsyncBlock: ^{
[array removeObject: theObj];
} withLock:array];
}
Which should always return immediately, since only the background threads will compete over the lock.
The problem is, this code crashes after executeBlock: without producing any output, error message, crash log, or any other useful thing. Is there something fundamentally flawed in my approach? If not, any suggestions with respect to why this might be crashing?
Edit:
Interestingly, it works without crashing if I simply do:
- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
void(^syncBlock)() = ^{
#synchronized(lock) {
block();
}
};
syncBlock();
}
But of course this will block the calling thread, which largely defeats the purpose. Is it possible that blocks do not cross thread boundaries? I would think not, since that would largely defeat the purpose of having them in the first place.
remember to call [block copy] otherwise it is not correctly retained because block are created on stack and destroyed when exit scope and unless you call copy it will not move to heap even retain is called.
- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
block = [[block copy] autorelease];
void(^syncBlock)() = ^{
#synchronized(lock) {
block();
}
};
syncBlock = [[syncBlock copy] autorelease];
[self performSelectorInBackground:#selector(executeBlock:) withObject:syncBlock];
}

Pausing iteration of a for loop to wait for user input

I wrote a for loop which is iterating through an array of objects.
Now I am asking myself if it's possible to break the iteration of the loop until the user clicks on a button which calls a IBAction?
for (int i = 0; i < [array count]; i++) {
// do something with the object
// wait for action method called
// user clicked action so go on
}
You can adapt the code to fit your case. It basically "unrolls" the loop into multiple messages. Start the sequence with [self doItForIndex:[NSNumber numberWithInt:0]];
- (BOOL)canDoitForIndex:(NSNumber *)i {
// return YES if you want to go ahead
// (e.g. test a BOOL you set in response to the user tapping a button
}
- (void)waitForIndex:(NSNumber *)i {
if ([self canDoItForIndex:i]) {
// do anything to clean up for i
// then repeat for i+1:
[self doItForIndex:[NSNumber numberWithInt:[i intValue]+1]];
} else {
[self performSelector:_cmd withObject:i afterDelay:0.01f;
}
}
- (void)doItForIndex:(NSNumber *)i {
if ([i intValue] < lastIndex) {
// do what you have to do
[self waitForIndex:i];
}
// else you're done
}
Apple's NSRunLoop concept expects you to complete processing pretty quickly. If you tie up the main thread by waiting for something, nothing else in your app can happen. The above code breaks the "wait" into multiple message sends, and keeps your app responsive.
ODRM algorithm works very well.
I just changed this line :
[self performSelector:_cmd withObject:i afterDelay:0.01f];
with this :
[NSThread sleepForTimeInterval:0.25];
[NSThread detachNewThreadSelector:_cmd toTarget:self withObject:i];
As I had UI elements to be updated, it was better for we to force waiting to be in a background thread.

Confounding Cocoa problem — program hangs unless there’s an unrecognised method call

Bear with me, this one is hard to explain. I hope some hero out there knows what’s going on here. Some history needed;
One of my cocoa objects, “Ball” represents a small graphic. It only makes sense within a view. In some of the Ball’s methods, it asks the view to redraw. Most importantly, it asks the view to redraw whenever the Ball’s position parameter is set. This is achieved in the setter.
Here’s the mouthful, as suggested:
In View.m
- (void)mouseUp:(NSEvent *)theEvent {
if (![runnerPath isEmpty]) {
[walkPath removeAllPoints];
[walkPath appendBezierPath:runnerPath];
[runnerPath removeAllPoints];
[[self held] setStep:0];
[[self held] setPath:walkPath];
[NSTimer scheduledTimerWithTimeInterval:.01 target:[self held] selector:#selector(pace) userInfo:nil repeats:YES];
}
}
In Ball.m
- (void)pace {
CGFloat juice = 10;
BOOL loop = YES;
while (loop) {
if ([self step] == [[self path] elementCount]) {
if ([[self timer] isValid]) {
[[self timer] invalidate];
}
[[self path] removeAllPoints];
// #throw([NSException exceptionWithName:#"test" reason:#"reason" userInfo:nil]);
}
if (loop) {
CGFloat distance;
NSPoint stepPoint;
if ([[self path] elementCount] > 0) {
NSPoint returnPoints[2];
[[self path] elementAtIndex:[self step] associatedPoints:returnPoints];
stepPoint = returnPoints[0];
distance = pixelDistance([self position], stepPoint);
}
if (distance <= juice) {
[self setPosition:stepPoint];
if (distance < juice) {
juice -= distance;
loop = YES;
[self setStep:[self step]+1];
} else {
loop = NO;
}
} else {
NSPoint cutPoint = moveAlongBetween([self position], stepPoint, juice);
[self setPosition:cutPoint];
loop = NO;
}
}
}
}
could you also tell how you handle exceptions? since normally an unrecognized selector will end your program. Maybe you need an exception rather than an unrecognized selector. Try:
#throw([NSException exceptionWithName:#"test" reason:#"reason" userInfo:nil]);
If this would fix it as well, you're doing something after this code which freezes the app.
edit: thanks for the code update.
There's some weird stuff going on here! I'm not going to rewrite the whole thing, so here's some pointers:
first of all: you're looping inside some routine that is called from a timer loop. Is that intended? There is no way to pause execution within that while() loop, so it will happen in a blink anyway. You would need to keep some state information in the class. E.g. adding a loop counter every time pace is called.
second: if you start a timer, it will call your selector with the timer as an argument. So define the function as -(void)pace:(NSTimer*)timer, and use timer, not [self timer] (the latter will not be your timer anyway, if you don't assign it!)
third: you're firing 100 times a second. That is a lot, and presumably higher than the refresh rate of any device you're writing this for. I think 20/sec is enough.
fourth: to be sure, if you change it to -(void)pace:(NSTimer*)timer, don't forget to use #selector(pace:) (i.e. don't forget the :)
fix those things, and if it's still broken, update your question again and put in comment so we will know. Good luck!
Try calling
for (NSView *each in [self views]) {
...
}
I'm assuming that views is an array, so fast enumeration applies to it directly and there is no need to call allObjects.
A couple of other points.
Have you set a Global breakpoint of objc_exception_throw? This will apply to all Xcode projects and is so useful I'm surprised it isn't set by default.
You say you looked at the Console for errors. I take it, then, that you didn't set a breakpoint on the code and step into it to see exactly what is happening when your execution reaches that point? Have a look at the Xcode Debugging Guide