In my application I have an object CameraHandler that uses GPUImage to detect certain movement from camera. It is initialized in my GameViewController.
It (CameraHandler) is able to successfully detect movements, and fire the relevant methods, however it locks up the GameViewController's view for a significant amount of time (~5 to 10 seconds) before any of the changes are displayed on screen. Once the CameraHandler detects a change, it fires a method that changes the background of the top view on the view controller and displays a UIAlertView (for testing purposes). Like I said, this only happens after 5-10 seconds from the moment it is called. I know the program itself is not frozen because I get the relevant log outputs from the methods. I've tried different techniques to try and fix this but I have come out empty handed for several weeks now.
In GameViewController (where I call and initiate the CameraHandler):
-(void)startRound{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
[_shotDetector captureStillImage];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(#"finish capture still image thread");
});
});
}
/* this method gets called from CameraHandler once it detects movement */
-(void)shotLifted:(NSNumber*)afterTime{
NSLog(#"shot lifted fired");
UIAlertView *lost = [[UIAlertView alloc]initWithTitle:#"Good Job!" message:#"Shot lifted in time" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[lost show];
[_questionView setBackgroundColor:[UIColor whiteColor]];
NSLog(#"shot lifted done");
}
CameraHandler.h
#interface CameraHandler : NSObject <GPUImageVideoCameraDelegate>
#property (strong) GPUImageOutput<GPUImageInput> *filter,*emptyFilter;
#property (strong) GPUImageVideoCamera *videoCamera;
#property (strong) GPUImagePicture *sourcePicture;
#property (strong) GPUImageOutput *pictureOutput;
#property (strong) GPUImageStillCamera *stillCamera;
#property (strong) __block UILabel *shotRemovedLabel;
#property (strong) __block NSDate *startTime;
#property (strong) NSMutableArray *averageLum;
#property (strong) id delegate;
#property (strong) GPUImageLuminosity *lumin;
CameraHandler.m - relevant method
-(void)startBlackoutShotMotionAnalysis{
NSLog(#"starting shot motion analysis");
[_videoCamera addTarget:self.filter];
[_sourcePicture processImage];
[_sourcePicture addTarget:self.filter];
[_videoCamera startCameraCapture];
_lumin = [[GPUImageLuminosity alloc] init];
[self.filter addTarget:_lumin];
__block int i =0;
__unsafe_unretained GameViewController* weakDelegate = self.delegate;
//begin luminosity detecting of live-video from uiimage
[(GPUImageLuminosity *)_lumin setLuminosityProcessingFinishedBlock:^(CGFloat luminosity, CMTime frameTime) {
if(i<60){
if(i>10){
_startTime = [NSDate date];
[_averageLum addObject:[NSNumber numberWithFloat:luminosity]];
}
i++;
}
else{
CGFloat average = [[_averageLum valueForKeyPath:#"#avg.floatValue"]floatValue];
CGFloat difference = fabsf(luminosity-average);
if(difference > 0.05){
NSTimeInterval liftedAfter = [_startTime timeIntervalSinceDate:[NSDate date]];
[weakDelegate performSelector:#selector(shotLifted:) withObject:[NSNumber numberWithFloat:liftedAfter]];
[_videoCamera stopCameraCapture];
NSLog(#"should turn white now");
return ;
}
}
}];
NSLog(#"finished returning executing starBlackoutMotionAnalysis Method");
}
NSLOG OUTPUT:
2014-04-08 20:22:45.450 Groupy[2887:5c0f] starting shot motion analysis
2014-04-08 20:22:46.152 Groupy[2887:5c0f] finished returning executing starBlackoutMotionAnalysis Method
2014-04-08 20:22:48.160 Groupy[2887:1303] shot lifted fired
2014-04-08 20:22:48.221 Groupy[2887:1303] shot lifted done
2014-04-08 20:22:48.290 Groupy[2887:1303] should turn white now
Any help in the right direction would be huge. I've been struggling with figuring this out.Thanks!
The first thing I usually look for when I see unusually delayed updates in the UI is whether or not my UI updating code is being executed on the main queue. Apart from a few exceptions, you should always dispatch any UI-related code to the main queue, or you'll get weird behaviour like this.
From what I can see, you perform the shotLifted: selector directly from within the luminosityProcessingFinishedBlock. We can safely assume that GPUImage will be calling that block off the main thread. This means that your code to initialise and show the alert view is happening off the main thread too.
To change this, you should try wrapping your call to shotLifted: in a block and dispatch that to the main queue:
dispatch_async(dispatch_get_main_queue(), ^{
[weakDelegate performSelector:#selector(shotLifted:) withObject:obj];
}
Or alternatively you can do:
[weakDelegate performSelectorOnMainThread:#selector(shotLifted:) withObject:obj waitUntilDone:NO];
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.
Here is the code
#interface ViewController ()
#property (nonatomic, strong) NSOperationQueue *queue;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_queue = [[NSOperationQueue alloc] init];
NSBlockOperation *aBlockOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation* aWeakBlockOperation = aBlockOperation;
[aBlockOperation addExecutionBlock:^{
NSLog(#"queue should still have the operation. And it does. yay!: %#", [_queue operations]); // This should print correctly. It will show the NSBlock operation correctly residing inside the NSOperationQueue
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"Now queue is empty??: %#", [_queue operations]); // This should print as being empty
NSLog(#"And a weak block is nil???: %#", aWeakBlockOperation); // This should print out **nil**
if (![aWeakBlockOperation isCancelled]) {
// Now i have no way to check the operation
}
});
}];
[_queue addOperation:aBlockOperation];
#end
[Edit]
The goal is to have a user interaction like this:
There is a tableView on screen with 5 or more cells. When ever a user click a cell, background process will perform background process that will take a while. The App will, at 3 second intervals, check to see if the user clicked on another cell. If the user clicked on another cell, I should cancel the current operation from queue, and begin processing the new one the user clicked on.
From the code above i have 2 problems i cant solve.
How do i make it so that my weak reference isnt deallocated in the dispatch_after block? The goal of putting it there is to pause the app for exactly 3 seconds. If dispatch_after is incorrect, then what code do i use there to prevent it becoming nil?
Why is it that my NSOperationQueue become empty after I call dispatch_after? Is there a way to make it not become empty?
dispatch_after schedules the block and returns immediately. So, your NSBlockOperation's executionBlock has almost no work to do — it immediately finishes and is removed from the queue. At that time, the operation is released and so the weak reference becomes nil before the dispatch_after block is called later.
If you do the dispatch_after first and schedule the operation from inside that block, it might suit your needs. You could just use sleep, but I wouldn't recommend that since you will be unnecessarily blocking a thread. See this question for more discussion on NSOperation and delays.
You can schedule operation inside the dispatch_after block and declare aBlockOperation as an instance variable/property so aWeakBlockOperation will not became nil.
But you do not need to hassle with the NSBlockOperation to achieve your goal. You can use dispatch_block_t instance variable which you would set to a new value (block with your code you need to be executed after the column is clicked) each time the column is clicked:
#implementation ViewController
{
dispatch_block_t columnBlock;
}
- (void)columnClicked
{
columnBlock = ^{ ... your code ... };
__weak dispatch_block_t weakColumnBlock = columnBlock;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_block_t colBlock = weakColumnBlock;
if (colBlock)
colBlock();
});
}
Could someone explain why following my code crash? Crash happenes inside the block in foo method.
I've got EXC_BAD_ACCESS or "error for object: double free". And I also got "-[NSObject description]: message sent to deallocated instance" when I set "Enable Zombie Objects" ON.
#interface ViewController ()
#property (nonatomic, strong) NSObject *obj;
#end
#implementation ViewController
// just adding button
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
[btn setTitle:#"test" forState:UIControlStateNormal];
btn.frame = CGRectMake(100, 100, 100, 100);
[btn addTarget:self action:#selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
// fired by button
- (void)btnAction:(id)sender {
for (int i = 0; i < 100; i++) {
[self foo];
}
}
// I want to understand this method
- (void)foo {
NSLog(#"foo");
self.obj = NSObject.new;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"%#", [[self obj] description]); // sometimes crash happenes here with a message "-[NSObject description]: message sent to deallocated instance"
});
}
#end
Looks like self.obj is deallocated between [self obj] and [obj description]. But I'm not sure why.
I think the object from [self obj] should be owned by it's scope and should not be deallocated even if self.obj = NSObject.new is executed at the same time on other threads.
Is my understanding wrong?
I'm testing on iOS 7.0.4 with ARC. Thanks!
You have a for loop that is calling your -foo method, so self.obj is rapidly getting set to new values. Each time this happens, you're executing code asynchronously that is accessing your (nonatomic) property. But even if it is always getting a correct value for that property when being accessed from multiple threads, the main thread is very likely setting the property to a new value before the background thread finishes using the previous value of the property. And once the property gets changed to a new value, it releases the previous object that was assigned to it.
Since you're accessing your property from multiple threads, you want it to be atomic, not nonatomic, so change your property to this:
#property (strong) NSObject *obj;
atomic is the default. It is probably also safer to do the following with your asynchronous block:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSObject *obj = self.obj;
if (self.obj) {
NSLog(#"%#", [obj description]);
}
});
You should no longer see a crash if you do this, because obj will always either be nil or a valid object with a strong reference to it inside the block.
However, you probably won't get the results you expect from this. For each execution of your asynchronous block, it's not guaranteed that you'll get the subsequent instances of NSObject that you're creating. There might be times where it executes your block where obj is the same object both times, and where you never see some of the objects that were created. This is because your asynchronous block isn't getting the instance set immediately before you made the call to invoke the block, it's getting it from the property. If you want this to use the instance set immediately prior, you must do something like the following:
__block NSObject *obj = NSObject.new;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"%#", [obj description]);
});
This should always use the instance that you created specifically for that invocation of the asynchronous block.
I suspect the issue is caused by the nonatomic property attribute as you are re-allocating self.obj 100 times I think there is a possibility of the background thread reading a partially reallocated object pointer.
Please try with:
#property (atomic, strong) NSObject *obj;
By the time the background logging is being done, self.obj could be different or in the middle of being changed.
Use a local variable like this:
- (void)foo {
NSLog(#"foo");
NSObject *val = [NSObject new];
self.obj = val;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"%#", val);
});
}
This will avoid threading issues and ensure the NSLog logs the proper instance.
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 have a UITextfield and a UIButton. The user can enter, for example, search word such as "dog" or "cat" and it will trigger a method in another class that runs on a custom dispatch GCD queue to fetch the images (around 100 or so).
Everything works fine, except if the user in the midst of fetching, decides to change and enter another search word such as "cat" and then press the fetch button, I would like to be able to stop that thread / method while it is fetching the images from the previous search term.
I have thought about NSThread (something I never used before) or blocks (to get notified once the method has finished running), but the problem with blocks is, I will get notified once the method had finished doing its thing, but what I need here is to tell it to stop fetching (because the user has decided on another search and entered another search term).
Can someone please cite me with some samples, as to how we can be able to stop a loop / method while it is running on a custom GCD thread before it is finished? Thanks in advance.
I'm using NSOperationand NSOperationQueue to cluster markers on a map in the background and to cancel the operation if necessary.
The function to cluster the markers is implemented in a subclass of NSOperation:
ClusterMarker.h:
#class ClusterMarker;
#protocol ClusterMarkerDelegate <NSObject>
- (void)clusterMarkerDidFinish:(ClusterMarker *)clusterMarker;
#end
#interface ClusterMarker : NSOperation
-(id)initWithMarkers:(NSSet *)markerSet delegate:(id<ClusterMarkerDelegate>)delegate;
// the "return value"
#property (nonatomic, strong) NSSet *markerSet;
// use the delegate pattern to inform someone that the operation has finished
#property (nonatomic, weak) id<ClusterMarkerDelegate> delegate;
#end
and ClusterMarker.m:
#implementation ClusterMarker
-(id)initWithMarkers:(NSSet *)markerSet delegate:(id<ClusterMarkerDelegate>)delegate
{
if (self = [super init]) {
self.markerSet = markerSet;
self.delegate = delegate;
}
return self;
}
- (void)main {
#autoreleasepool {
if (self.isCancelled) {
return;
}
// perform some Überalgorithmus that fills self.markerSet (the "return value")
// inform the delegate that you have finished
[(NSObject *)self.delegate performSelectorOnMainThread:#selector(clusterMarkerDidFinish:) withObject:self waitUntilDone:NO];
}
}
#end
You could use your controller to manage the queue,
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.name = #"Überalgorithmus.TheKillerApp.makemyday.com";
// make sure to have only one algorithm running
self.operationQueue.maxConcurrentOperationCount = 1;
to enqueue operations, kill previous operations and the like,
ClusterMarker *clusterMarkerOperation = [[ClusterMarker alloc] initWithMarkers:self.xmlMarkerSet delegate:self];
// this sets isCancelled in ClusterMarker to true. you might want to check that variable frequently in the algorithm
[self.operationQueue cancelAllOperations];
[self.operationQueue addOperation:clusterMarkerOperation];
and to respond to the callbacks when the operation has finished:
- (void)clusterMarkerDidFinish:(ClusterMarker *)clusterMarker
{
self.clusterMarkerSet = clusterMarker.markerSet;
GMSProjection *projection = [self.mapView projection];
for (MapMarker *m in self.clusterMarkerSet) {
m.coordinate = [projection coordinateForPoint:m.point];
}
// DebugLog(#"now clear map and refreshData: self.clusterMarkerSet.count=%d", self.clusterMarkerSet.count);
[self.mapView clear];
[self refreshDataInGMSMapView:self.mapView];
}
If I remember correctly I used this tutorial on raywenderlich.com as a starter.
I would recommend using NSOperation as it has cancel method which will cancel the current running operation.