is it necessary to use dispatch_queue at here - ios

Hopefully it is an appropriate question to ask. My goal is
1.add a controller into an array name 'arrControllers'
2.access and get current controller from 'arrControllers' to do sth with it
and I just wanna make sure the arrControllers should be ready before I access and use it. That is why I am using serial queue and dispatch_sycn like like following.
-(void)viewDidLoad
{
[super viewDidLoad];
firstViewController = [[FirstViewController alloc] initWithNibName:#"FirstViewController" bundle:nil];
[self setUpWithView:firstViewController];
}
and in setUpWithView: is
-(void)setUpWithView:(UIViewController*)viewController {
dispatch_queue_t queue;
queue = dispatch_queue_create("my queue", NULL);
containerController = [ContainerViewController sharedContainerController];
// What I am taking about is from now on
dispatch_sync(queue, ^{
[containerController initWithRootViewController:viewController];
});
dispatch_sync(queue, ^{
[containerController setUpView];
});
}
and initWithRootViewController: is
- (void)initWithRootViewController:(UIViewController*)rootViewController {
arrControllers = [[NSMutableArray alloc] init];
arrControllers = [NSMutableArray arrayWithObject:rootViewController];
}
and setUpView is
-(void)setUpView {
/* Process to ADD currentController TO hierarchy */
[self addChildViewController:[arrControllers lastObject]];
............................................................
}
As far as I know the compiler will execute codes line by line, it means by do following
-(void)setUpWithView:(UIViewController*)viewController {
containerController = [ContainerViewController sharedContainerController];
// Not using serial_queue and dispatch_sync
[containerController initWithRootViewController:viewController];// line1
[containerController setUpView]; //line2
}
compiler will execute line2 until it finished the task at line1.
My question is
1. is it necessary to using `serial_queue` and `dispatch_sycn`for this situation.
PS: if you think it is a bad question to ask. please comment what you think about it.

First of all, you should have a look at UIView and UIViewController life cycles, this will tell you when a UIVie or a UIViewController is "ready to be used". Second, you should check when the serial ques and GCD is used.
For the second point I can give you a short summary because it's kind of faster that the first point.
Use CGD and queues (other then main queue) if you want to do computing or other stuff, that don't involve UI changes, on a background thread without freezing the UI.
Use GCD with main queue to switch between a background queue and the main (UI) queue.
All UI operations must be performed on the main thread.
So in your case, you want to create a view controller and store it into an array, so as a suggestion, all the UI related calls of your view controllers should be performed on the UI thread, so no need for GCD or background threads.
In case you are doing some complicated stuffs on your view controller's init methods, just put those complicated stuffs on a GCD block on a separated thread.

In this situation if you do not use dispatch_sync, you will get exactly what you need:
-(void)setUpWithView:(UIViewController*)viewController {
containerController = [ContainerViewController sharedContainerController];
// Not using serial_queue and dispatch_sync
[containerController initWithRootViewController:viewController];// line1
[containerController setUpView]; //line2
}
Line 1 will execute initWithRootViewController:, going deeper and executing lines
arrControllers = [[NSMutableArray alloc] init];
arrControllers = [NSMutableArray arrayWithObject:rootViewController];
then it will return from this function one level above and proceed, moving to line 2 and going down to line
[self addChildViewController:[arrControllers lastObject]];
ALSO. Never write code like this
arrControllers = [[NSMutableArray alloc] init];
arrControllers = [NSMutableArray arrayWithObject:rootViewController];
First line creates an empty NSMutableArray and assigns a pointer to it into arrControllers.
Second line creates another array (autoreleased) and assigns a pointer to it in arrControllers again. So a link to array created in the first line is lost and it is leaked under manual memory management.
If you use manual memory management, do the following:
arrControllers = [[NSMutableArray alloc] init];
[arrControllers addObject:rootViewController];
OR
arrControllers = [[NSMutableArray alloc] initWithObjects:rootViewController,nil];
Under ARC leave only second line.
Hope I understood you correctly. Feel free to ask questions.

Related

How to Update Label With Downloading Data

In my model, data is downloaded from website in a for loop and in a each turn data is sending to my viewController using protocol method. In model file;
for(NSString* data in DataArray){
[self.delegate passUpdatingCourse:data_name];
//other operations
}
In my viewController data name coming from model is saving to NSArray property in other thread;
ModelClass *modelObject = [[ModelClass alloc] init];
[modelObject setDelegate:self];
dispatch_queue_t otherQ = dispatch_queue_create("Q", NULL);
dispatch_async(otherQ, ^{
//other operations
[self performSelectorOnMainThread:#selector(passUpdatingCourse:) withObject:nil waitUntilDone:YES];
dispatch_async(dispatch_get_main_queue(), ^{
[self.myIndicator stopAnimating];
self.indicatorText.hidden = YES;
[self.changingCourseLabel setNeedsDisplay];
});
});
And also data coming via protocol method is setting viewController label;
-(void)passUpdatingCourse:(NSString *)data_in{
self.myLabel.text = data_in;
}
When each data came, myLabel in a viewController must be update. But it is not happens. In a protocol method when I use this;
NSLog(#"Data:%#",self.myLabel.text);
Yeah it shows data in a console but myLabel in a view is not changing.
I searched questions like that but couldn't find a solution.
Assuming your loop is on a background thread, dispatch your label updates to the main queue (async).
If you're inside a for loop on the main thread nothing is going to get updated in the UI until your method returns and dispatch won't help in that case.

How to get an array out of Grand Central Dispatch?

I am a newbie. I am using Grand Central Dispatch to populate an array (student_temp) on another thread. That part is working fine. The problem is I cannot pass the array to a class property (student_Array) where it is used throughout the class. I can't get the array back on the main thread.
it works fine until I get back tot he main thread and I can't pass student_temp into student_Array (the property) either inside or outside of GCD.
What am I doing wrong, or is there a better to populate the array property using GCD?
Thank you for your help. And please try to explain in non-technical language if possible I am new at this.
- (void)viewDidLoad
{
[super viewDidLoad];
R2LFetcher *studentFetch = [[R2LFetcher alloc] init];
__block NSMutableArray *student_temp = [[NSMutableArray alloc] init];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
//long-running code goes hereā€¦
student_temp = [studentFetch fetchToStudentArray];
dispatch_async(dispatch_get_main_queue(), ^{
// code the updates the main thread (UI) here...
student_Array = student_temp;
});
});
student_Array = student_temp;
A couple of reactions:
In the last line of your code, you're setting student_Array to student_temp. Clearly that line makes no sense because you're populating student_temp asynchronously. And you're opening yourself up to synchronization issues if you're trying to simultaneously access the save variable in two queues. Don't bother to assign student_Array to student_temp at the end of viewDidLoad, but rather just do it inside the nested dispatch_async calls.
Inside the block, you're populating and setting student_temp. It probably makes more sense to make that variable scoped within that block, avoiding temptation to access it from outside that block as well as simplifying your code because the __block qualifier is no longer needed.
This block is running asynchronously, so when you update student_Array in the main queue, you might want to update your UI at the same time (e.g. reload the tableview or whatever). Perhaps you're doing that already and just removed it for the sake of brevity, but I just wanted to make sure.
Thus:
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
R2LFetcher *studentFetch = [[R2LFetcher alloc] init];
// long-running code goes here, for example ...
NSMutableArray *student_temp = [studentFetch fetchToStudentArray];
dispatch_async(dispatch_get_main_queue(), ^{
student_Array = student_temp;
// code the updates the main thread (UI) here, for example...
[self.tableView reloadData];
});
});
}
You should be able to add objects to student_Array directly from your block. Unlike stack variables, properties and ivars don't get copied when used inside a block. Instead, self gets retained in the block, and the property is referenced through it.
Of course, you need to be aware of concurrency issues, e.g. if you need to access the data from the main thread as well. For that, you probably still want to have this at the end of your async GCD block:
// done populating the data
dispatch_async(dispatch_get_main_queue(), ^{
// update the UI
}

Load UIImagePickerController faster?

I need to load UIImagePickerController faster. My app will call UIImagePickerController from possibly multiple controllers and within each controller there are two buttons, one for Photo Library, another for Camera. This is my sample app. Some solutions I tried are recommended by Apple in 'Concurrency Programming Guide'. The simplest is to alloc & init an instance when user presses a button. This can take up to 2 seconds before the camera shows up. Solution attempt:- (1) I made a #property (...) *imagePicker and called its alloc & init in viewDidAppear to make it seem smooth, but it interfered with loading of other elements, possibly because it was using the same thread. Worse results when used in initWithNibName or viewDidLoad. (2) Operation queues... not pleasing results. (3) Dispatch Queues... better results but still noticeable choppy performance. (4) performSelectorInBackground:withObject: not great results. I don't think I want to go global, unless otherwise recommended. I have no problem making two #properties of UIImagePickerController. I will continue to possibly 'Threading Programming Guide'. Please include any necessary memory management practices with your solutions, if you can. Thank you.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// (1) TRYING OPERATION QUEUES
// (a) --- NSBlockOperation
NSBlockOperation *NSBO_IP = [NSBlockOperation blockOperationWithBlock:^{
NSLog(#"before");
imagePicker = [[UIImagePickerController alloc] init];
// 1.9, 1.6, 1.4 seconds pass between 'before' and 'after'
// it seems this method has a dynamic technique, executes in different order every time
NSLog(#"after");
}];
// (b) --- NSInvocationOperation
NSInvocationOperation *NSIO_IP = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(loadTheImagePicker) object:nil];
// --- Add an operation
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//NSLog(#"time 1");
// (a) [queue addOperation:NSBO_IP];
// (b) [queue addOperation:NSIO_IP];
//NSLog(#"time 2");
// (2)TRYING DISPATCH QUEUES
// (a) --- GCD call (do I need to release 'myQueue' at some point since this is C-level call?)
/*
NSLog(#"time 1");
dispatch_queue_t myQueue = dispatch_queue_create("My Queue", NULL);
dispatch_async(myQueue, ^{
imagePicker = [[UIImagePickerController alloc] init];
// 1.2, 1.2, 1.2, 1.4 seconds significant increase over Operation Queues
// a solid constant executing technique, executes very consistently
});
NSLog(#"time 2");
*/
// (3)TRYING performSelectorInBackground:withObject (not recommended?)
NSLog(#"time 1");
[self performSelectorInBackground:#selector(loadTheImagePicker) withObject:nil];
// 1.3, 1.7, 1.3 seconds pass between 'before' and 'after'
NSLog(#"time 2");
// RESULTS REFLECTED IN LINE BELOW !
[self changeText:self]; // text does not change on time when trying to alloc & init an ImagePickerController
}
- (void)loadTheImagePicker
{
NSLog(#"before");
imagePicker = [[UIImagePickerController alloc] init];
// --- NSInvocationOperation used
// 1.6, 1.2, 1.4 seconds pass between 'before' and 'after'
// this method has a more constant executing technique, as far as order of executions
// --- NSInvocationOperation used
NSLog(#"after");
}
from #Murat's answer in a comment:
I found the solution. This question has already been answered. It
seems when connected to Xcode debugger the [UIImagePickerController
alloc] init] takes much longer to execute than when running the App
without Xcode.
My solution is still to keep #property and do the alloc
& init in the viewDidLoad method.
The other solution is here:
UIViewController - mysteriously slow to load
moved this to an answer as I almost missed it as a comment.

Objective C- Trouble updating UI on main thread

I am having some trouble updating my UI using performSelectorOnMainThread. Here is my situation. In my viewDidLoad I set up an activity indicator and a label. Then I call a selector to retrieve some data from a server. Then I call a selector to update the UI after a delay. Here's the code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.reloadSchools = [[UIAlertView alloc] init];
self.reloadSchools.message = #"There was an error loading the schools. Please try again.";
self.reloadSchools.title = #"We're Sorry";
self.schoolPickerLabel = [[UILabel alloc]init];
self.schoolPicker = [[UIPickerView alloc] init];
self.schoolPicker.delegate = self;
self.schoolPicker.dataSource = self;
self.server = [[Server alloc]init];
schoolList = NO;
_activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self.view addSubview:_activityIndicator];
[self.view bringSubviewToFront:_activityIndicator];
[_activityIndicator startAnimating];
[NSThread detachNewThreadSelector: #selector(getSchoolList) toTarget: self withObject: nil];
[self performSelector:#selector(updateUI) withObject:nil afterDelay:20.0];
}
The selector updateUI checks to see if the data was retrieved, and calls a selector on the main thread to update the UI accordingly. Here is the code for these parts:
-(void)updateUI
{
self.schools = [_server returnData];
if(!(self.schools == nil)) {
[self performSelectorOnMainThread:#selector(fillPickerView) withObject:nil waitUntilDone:YES];
}
else {
[self performSelectorOnMainThread:#selector(showError) withObject:nil waitUntilDone:YES];
}
}
-(void)showError {
NSLog(#"show error");
[_activityIndicator stopAnimating];
[self.reloadSchools show];
}
-(void)fillPickerView {
NSLog(#"fill picker view");
schoolList = YES;
NSString *schoolString = [[NSString alloc] initWithData:self.schools encoding:NSUTF8StringEncoding];
self.schoolPickerLabel.text = #"Please select your school:";
self.shoolArray = [[schoolString componentsSeparatedByString:#"#"] mutableCopy];
[self.schoolPicker reloadAllComponents];
[_activityIndicator stopAnimating];
}
When the selector fillPickerView is called the activity indicator keeps spinning, the label text doesn't change, and the picker view doesn't reload its content. Can someone explain to me why the method I am using isn't working to update my ui on the main thread?
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//load your data here.
dispatch_async(dispatch_get_main_queue(), ^{
//update UI in main thread.
});
});
First of all you should not be using detachNewThreadSelector. You should use GCD and submit your background task to an async queue. Threads are costly to create. GCD does a much better job of managing system resources.
Ignoring that, your code doesn't make a lot of sense to me. You submit a method, getSchoolList, to run on a background thread. You don't show the code that you are running in the background.
Then use performSelector:withObject:afterDelay to run the method updateUI on the main thread after a fixed delay of 20 seconds.
updateUI checks for self.schools, which presumably was set up by your background thread, and may or may not be done. If self.schools IS nil, you call fillPickerView using performSelectorOnMainThread. That doesn't make sense because if self.schools is nil, there is no data to fill the picker.
If self.schools is not nil, you display an error, again using performSelectorOnMainThread.
It seems to me that the logic on your check of self.schools is backwards. If it is nil you should display an error and if it is NOT nil you should fill the picker.
Next problem: In both cases you're calling performSelectorOnMainThread:withObject:waitUntilDone: from the main thread. Calling that method from the main thread doesn't make sense.
Third problem: It doesn't make sense to wait an arbitrary amount of time for a background task to run to completion, and then either succeed or fail. You won't have any idea what's going on for the full 20 seconds. If the background task finishes sooner, you'll never know.
Instead, you should have your background task notify the main thread once the task is done. That would be a valid use of performSelectorOnMainThread:withObject:waitUntilDone:, while calling it from the main thread is not. (Again, though, you should refactor this code to use GCD, not using threads directly.
It seems pretty clear that you are in over your head. The code you posted needs to be rewritten completely.

iOS NSOperation subclass freezes device rotation on execution

I need your help. I have write my own custom NSOperation class called GetNewsOperation. I call it like this:
GetNewsOperation *getNewsOperation = [[GetNewsOperation alloc] initWithLocalNewsCategories:self];
[loadNewsOperationQueue addOperation:getNewsOperation];
[getNewsOperation release];
In GetNewsOperation class I have implemented init method for initialization and main method for executing operation and returning data back to the main thread.
Main method looks like this:
- (void)main {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
AppSettingsController *sharedAppSettingsController = [AppSettingsController sharedAppSettingsController];
if( [type isEqualToString:#"getCategory"] ) {
NSMutableArray *parsedData = [[NSMutableArray alloc] initWithArray:[sharedAppSettingsController getLocalNewsCategories]];
[newsViewController performSelectorOnMainThread:#selector(loadDataResponse:) withObject:[NSArray arrayWithObjects:parsedData, nil] waitUntilDone:NO];
[parsedData release]; parsedData = nil;
}
[pool release];
}
Everything works fine but I have a minor problem. When this operation is called application does not rotate on device orientation change. It changes after operation is finished.
As far as I know this operation is running for sure in new thread (not in main) cos all other elements in the app are active (not freezed). But I have only problem with orientation. The app looks kind a crappy if application does not rotate only when this operation occurs...
How can I solve this?
Thanks!
Actually it works like predicted. I was doing something else on main thread that blocked application.

Resources