setNeedsDisplay not working inside a block - ios

I'm using CMMotionManager for retrieving accelerometer data. The thing is that the accelerometer data gets printed periodically, the instance variables are changed in the view, but the view doesn't get redrawn. I have checked that hv is not nil and that everything is hooked. Is there a problem with calling setNeedsDisplay within a block?
-(void) viewDidAppear:(BOOL) animated
{
[super viewDidAppear: animated];
[motionManager startAccelerometerUpdatesToQueue:motionQueue withHandler:
^(CMAccelerometerData *accelerometerData, NSError *error)
{
NSLog(#"%#",accelerometerData);
HypnosisView *hv = (HypnosisView *) [self view];
hv.xShift = 10.0 * accelerometerData.acceleration.x;
hv.yShift = -10.0 * accelerometerData.acceleration.y;
[hv setNeedsDisplay];
}];
}

It's because your calling a UI method on a thread different from the main thread.
Add this to your block:
dispatch_async(dispatch_get_main_queue(), ^{
[hv setNeedsDisplay];
});
Remember that any method dealing with user interface elements must be called from the main thread.

I have done the same in other blocks and it did work, though not with the callback you use here. Maybe the block is not executed on the main thread? You can check that with:
NSLog(#"Main thread? %d", [NSThread isMainThread]);
If it is not, you could force setNeedsDisplay to run on the main thread.

Just wanted to test #cocoahero's answer. I tried to call setTitle for a UIButton in a non main thread. And the title did changed. I tried this both targeting iOS 5.1 and 7.0 using Xcode 5.0.1. Also I called setNeedsDisplay from a non main thread and it worked too.
[NSThread isMainThread] was how I made sure that my calls were not from main thread. I am not sure calling from non main thread was the cause to your problem but at least there are other possibilities there. You can take a look at my answer for another question.

Related

Wait for value to change in thread Objective C

I have a thread call in objective C and i want once this thread ends i want to return a value ;the value will be changed inside the thread
So the method must not return the value unless the tread ends
Here is the code i use:
[NSThread detachNewThreadSelector:#selector(CheckBeforePrint2) toTarget:self withObject:nil];
This is My Full Code
- (NSString *) getStatusPrinter:(NSString *) printerSerialNumber
{
self.printerSN = printerSerialNumber;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *Result = #"-1";
[NSThread sleepForTimeInterval:2.0f];
[NSThread detachNewThreadSelector:#selector(CheckBeforePrint) toTarget:self withObject:Result];
[pool release];
return Result;
}
Now i want to wait for the value of Result and return it i am using
cocoa
And i am returning the value to another app
Can anyone help in that.
Thanks
What you are doing here requires the use of a semaphore for example. If there is nothing more to it than you are providing here then a completion block to a background running method is the best way to do it. See option 2 below
Either way, why do you want the parent thread (the thread dispatching a new thread) to wait for another thread? If the parent thread is waiting, it is locked until the dispatched thread is done (according to your requirement). This situation is redundant because the whole point of dispatching another thread is so that the parent thread can continue with other things. Unless of course the parent thread needs to wait for multiple threads, then it makes sense to lock it.
Having said that, its best to just let the dispatching thread / parent thread do the processing that you are dispatching on to another thread. Im only saying this given the details you have provided.
OPTION 1 use a semaphore
Use a semaphore to lock and unlock parent thread
-(void)getStatusPrinter()
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[NSThread detachNewThreadSelector:#selector(checkBeforePrint2) toTarget:self withObject: semaphore];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self print]; // this will run after semaphore is unlocked
}
-(void)checkBeforePrint2:(dispatch_semaphore_t)sem
{
//this is within child thread
//do some processing,
dispatch_semaphore_signal(sem);//unlock semaphore
}
But, as I mentioned before, this situation seems redundant because the parent thread waits (therefore unusable) for child thread; why can't it just do the work itself...
OPTION 2 use completion block (PREFERABLE)
Use a completion block that you pass to the child thread. This allows the parent thread to continue. If it is the main thread it remains free for UI stuff.
-(void)getStatusPrinter()
{
[self checkBeforePrint2WithCompletion: ^{
[self print];
}];
//continue with more stuff
}
-(void)checkBeforePrint2WithCompletion:(void (^ __nullable)(void))completion
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//do something before executing completion code
if(completion){
completion();
}
});
}
Disclaimer: there may be typos in this code as it was not written in an editor/ IDE. Please comment if any.
UPDATE in response to added details.
Okay, for the fact that you said you need to return the result to another application, that means the entry thread at getStatusPrinter can not be allowed to return after dispatching a new thread. If you really need to create a new thread for CheckBeforePrint then the entry thread has to wait. That to me is pointless. You can simply run everything on the entry thread.
If you are using openURL:options:completionHandler: then the entry thread doesn't need to wait. The value of result can be passed back within the completion block.
Please refer to Apple's documentation on openURL with a completion handle

Why does a strong reference to parent UIViewController in performBatchUpdates leak an activity?

I just finished debugging a very nasty UIViewController leak, such that the UIViewController was not dealloc'd even after calling dismissViewControllerAnimated.
I tracked down the issue to the following block of code:
self.dataSource.doNotAllowUpdates = YES;
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:#[indexPath]];
} completion:^(BOOL finished) {
self.dataSource.doNotAllowUpdates = NO;
}];
Basically, if I make a call to performBatchUpdates and then immediately call dismissViewControllerAnimated, the UIViewController gets leaked and the dealloc method of that UIViewController never gets called. The UIViewController hangs around forever.
Can someone explain this behavior? I assume performBatchUpdates runs over some time interval, say, 500 ms, so I would assume that after said interval, it would call these methods and then trigger the dealloc.
The fix appears to be this:
self.dataSource.doNotAllowUpdates = YES;
__weak __typeof(self)weakSelf = self;
[self.collectionView performBatchUpdates:^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
[strongSelf.collectionView reloadItemsAtIndexPaths:#[indexPath]];
}
} completion:^(BOOL finished) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (strongSelf) {
strongSelf.dataSource.doNotAllowUpdates = NO;
}
}];
Note that the BOOL member variable, doNotAllowUpdates, is a variable I added that prevents any kind of dataSource / collectionView updates while a call to performBatchUpdates is running.
I searched around for discussion online about whether or not we should use the weakSelf/strongSelf pattern in performBatchUpdates, but did not find anything specifically on this question.
I am happy that I was able to get to the bottom of this bug, but I would love a smarter iOS developer to explain to me this behavior I am seeing.
This seems like a bug with UICollectionView. API users should not expect single-run block parameters to be retained beyond the execution of the task, so preventing reference cycles should not be an issue.
UICollectionView should be clearing up any references to blocks once it has finished the batch update process, or if the batch update process is interrupted (for example, by the collection view being removed from the screen).
You've seen for yourself that the completion block is called even if the collection view is taken off-screen during the update process, so the collection view should then be nilling out any reference it has to that completion block - it will never be called again, regardless of the current state of the collection view.
As you figured out, when weak is not used a retain cycle is created.
The retain cycle is caused by self having a strong reference to collectionView and collectionView now has a strong reference to self.
One must always assume that self could have been deallocated before an asynchronous block is executed. To handle this safely two things must be done:
Always use a weak reference to self (or the ivar itself)
Always confirm weakSelf exists before passing it as a nunnull
param
UPDATE:
Putting a little bit of logging around performBatchUpdates confirms a lot:
- (void)logPerformBatchUpdates {
[self.collectionView performBatchUpdates:^{
NSLog(#"starting reload");
[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
NSLog(#"finishing reload");
} completion:^(BOOL finished) {
NSLog(#"completed");
}];
NSLog(#"exiting");
}
prints:
starting reload
finishing reload
exiting
completed
This shows that the completion block is fired AFTER leaving the current scope, which means it is dispatched asynchronously back to the main thread.
You mention that you immediately dismiss the view controller after doing the batch update. I think this is the root of your issue:
After some testing, the only way I was able recreate the memory leak was by dispatching the work before dismissing. It's a long shot, but does your code look like this by chance?:
- (void)breakIt {
// dispatch causes the view controller to get dismissed before the enclosed block is executed
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadItemsAtIndexPaths:[self.collectionView indexPathsForVisibleItems]];
} completion:^(BOOL finished) {
NSLog(#"completed: %#", self);
}];
});
[self.presentationController.presentingViewController dismissViewControllerAnimated:NO completion:nil];
}
The code above leads to dealloc not being called on the view controller.
If you take your existing code and simply dispatch (or performSelector:after:) the dismissViewController call you will likely fix the issue as well.

ios using release in an async block

I have an IOS project (ARC disabled) which has several view controllers. One particular controller initialises a member object of type MyClass, however when the view controller is dismissed, I'm calling a cleanup method for the object which uses a thread (using dispatch-async) to make some time consuming operations and then when these operations are done im executing a [self release] on the main queue for the object. Is this a good practise, will it cause any errors? Below is a similar example to what im doing:
#implementation ViewController
- (void)viewDidLoad
{
myObj = [[MyClass alloc] init];
}
-(void)viewWillDisappear
{
[myObj cleanup];
}
#end
//myClass
#implementation MyClass
- (void)cleanup()
{
dispatch_queue_t myQueue = dispatch_queue_create ("MyClassDeallocQueue", NULL);
dispatch_async(myQueue, ^{
//time consuming operations
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
}
#end
Is this a good practise, will it cause any errors?
Currently, your code has an unbalanced retain/release. That is definitely an error (over release).
"Is it good practice?" - well, I don't know what you are trying to accomplish. But if your goal is to keep self alive, until after the block is executed, it is already accomplished purely through the fact that self will be captured. So, strictly a release is not needed.
However, if you NOT explicitly release self on the main thread, you introduce a subtle bug: it might happen that the block has the last reference to self, and since it may execute on some arbitrary thread, it will release self on this non-main thread. And this is forbidden: UIKit methods (including dealloc) MUST be called on the main thread!
Thus, it might make sense:
[self retain];
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self release];
});
});
or shorter:
dispatch_async(myQueue, ^{
// time consuming operation, which captures `self`
[self doSomething];
...
// ensure that `dealloc` will be executed on the main thread, if
// last reference is held by the block:
dispatch_async(dispatch_get_main_queue(), ^{
[self self];
});
});
Edit:
It's an interesting question, whether the "short" version is actually tread-safe or has a race:
Suppose, self will be released in the block executed on myQueue, as the effect of capturing self before it will be retained in the same bock as an effect of capturing self for the block executed on the main queue. Then, we have an issue. Comments appreciated.

UI not updating in main thread

This is really driving me nuts.
I have a button, and if that button is touched it will call a method that updates the UI. Here's the method in question:
- (void)loadLevelWithImagePath:(NSString *)imagePath difficulty:(int)difficulty modelName:(NSString *)modelName
{
// do stuffs
}
Except that it doesn't.
However when I enclose the whole method body in:
dispatch_async(dispatch_get_main_queue(), ^{
// do stuffs
}
It works!
However, I am baffled because when I put a breakpoint in the method, according to the debugger it is already in the main thread. Also If I put these 2 checks:
- (void)loadLevelWithImagePath:(NSString *)imagePath difficulty:(int)difficulty modelName:(NSString *)modelName
{
NSLog(#"%d", [NSThread currentThread] == [NSThread mainThread]);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%d", [NSThread currentThread] == [NSThread mainThread]);
// do stuffs
}
}
Both returns TRUE!
So my question is, why is the UI not updating? Thanks a lot!
It could be a nil problem, however, you provide too little info to know for sure. The reason it works with dispatch_async and doesn't without is not necessarily the thread you're calling the methods from, it could be that, at the time when you call the code, some of your UI objects are nil. When you call dispatch_async you add the job to the queue but since all dispatch queues are first-in, first-out data structures, you actually add the job at the end of the run loop, which potentially gives time for initialisation (wherever that is done)

How can I get back into my main processing thread?

I have an app that I'm accessing a remote website with NSURLConnection to run some code and then save out some XML files. I am then accessing those XML Files and parsing through them for information. The process works fine except that my User Interface isn't getting updated properly. I want to keep the user updated through my UILabel. I'm trying to update the text by using setBottomBarToUpdating:. It works the first time when I set it to "Processing Please Wait..."; however, in the connectionDidFinishLoading: it doesn't update. I'm thinking my NSURLConnection is running on a separate thread and my attempt with the dispatch_get_main_queue to update on the main thread isn't working. How can I alter my code to resolve this? Thanks! [If I need to include more information/code just let me know!]
myFile.m
NSLog(#"Refreshing...");
dispatch_sync( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self getResponse:#"http://mylocation/path/to/file.aspx"];
});
[self setBottomBarToUpdating:#"Processing Please Wait..."];
queue = dispatch_queue_create("updateQueue", DISPATCH_QUEUE_CONCURRENT);
connectionDidFinishLoading:
if ([response rangeOfString:#"Complete"].location == NSNotFound]) {
// failed
} else {
//success
dispatch_async(dispatch_get_main_queue(),^ {
[self setBottomBarToUpdating:#"Updating Contacts..."];
});
[self updateFromXMLFile:#"http://thislocation.com/path/to/file.xml"];
dispatch_async(dispatch_get_main_queue(),^ {
[self setBottomBarToUpdating:#"Updating Emails..."];
});
[self updateFromXMLFile:#"http://thislocation.com/path/to/file2.xml"];
}
In my connectionDidFinishLoading: I would try something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^ {
if ([response rangeOfString:#"Complete"].location == NSNotFound]) {
// failed
} else {
//success
dispatch_async(dispatch_get_main_queue(),^ {
[self setBottomBarToUpdating:#"Updating Contacts..."];
});
[self updateFromXMLFile:#"http://thislocation.com/path/to/file.xml"];
dispatch_async(dispatch_get_main_queue(),^ {
[self setBottomBarToUpdating:#"Updating Emails..."];
});
[self updateFromXMLFile:#"http://thislocation.com/path/to/file2.xml"];
}
});
Then all that file access is happening in a background queue so the main queue is not locked up. The main queue will also complete this call to connectionDidFinishLoading much more quickly, since you're throwing all the hard work onto the default queue instead, which should leave it (and the main thread) ready to accept your enqueuing of the updates to the UI which will be done by the default queue as it processes the block you just enqueued to it.
The queue handover becomes
main thread callback to connectionDidFinishLoad:
rapid handoff to default global queue releasing main thread
eventual hand off to main queue for setBottomBarToUpdating: calls
performing main queue blocks on main thread to properly update UI
eventual completion of blocks on main queue
eventual completion of blocks on default queue
You've increased concurrency (good where you've good multi-core devices) and you've taken the burden of I/O off the main thread (never a good place for it) and instead got it focused on user interface work (the right place for it).
Ideally you woud run the NSURLConnection run loop off the main thread too, but this will might be enough for you to get going.
Which run loop are you running the NSURLConnection in? If it's the main loop, you're queueing up the setBottomBarToUpdating: calls behind the work you're already doing, hence the probable reason why you're not seeing the UI update.
You could also give performSelectorOnMainThread try like so:
if ([response rangeOfString:#"Complete"].location == NSNotFound]) {
// failed
} else {
//success
[self performSelectorOnMainThread:#selector(setBottomBarToUpdating) withObject:#"Updating Contacts..." waitUntilDone:false];
[self updateFromXMLFile:#"http://thislocation.com/path/to/file.xml"];
[self performSelectorOnMainThread:#selector(setBottomBarToUpdating) withObject:#"Updating Emails..." waitUntilDone:false];
[self updateFromXMLFile:#"http://thislocation.com/path/to/file2.xml"];
}

Resources