How can I wait for some method to complete and then continue work ??
- (void)loadMoreDataOnBottomOfTableview
{
NSLog(#"LOADING ITEMS ON BOTTOM");
[self refreshStream];
[self.mainTableView reloadData];
...
}
So I need to wait refreshStream method to complete and then reload tableview data and rest of loadMoreDataOnBottomOfTableview (...).
Use a completion block. That's what they were designed for.
See the completion handler section in this guide. http://developer.apple.com/library/ios/#featuredarticles/Short_Practical_Guide_Blocks/index.html
Redefine refreshStream
-(void)refreshStream:(void (^)(void))complete;
-(void)loadMoreDataOnBottomOfTableview
{
[self refreshStream:^{
[self.mainTableView reloadData];
}];
}
This should do you right also check out this page, using typedef is the unspoken standard.
http://developer.apple.com/library/mac/#featuredarticles/BlocksGCD/_index.html
[self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];
You can call your method using this.
I can answer your query in swift. Similarly you can use completion block in your code to achieve the task.
class TestClosure
{
func calculateAdditionData() {
countNumbers({ (result) -> Void in
println("[self refreshStream] completed")
//[self.mainTableView reloadData];
})
}
func refreshStream(completion: (() -> Void)!) {
//Your refresh code
completion()
}
}
Completion blocks/Closures are a proper way to wait for something to complete.
You can use performSelectorOnMainThread:withObject:waitUntilDone: as follows:
[self performSelectorOnMainThread:#selector(refreshStream) withObject:nil waitUntilDone:YES]
[self.mainTableView reloadData];
Note however that this is NOT a recommended design pattern. Async calls should use callbacks (your refreshStream method should call back to a method in your view controller which should then trigger reloadData
Related
I am wondering how to do the following correctly: I have a method that is to return an NSData object. It gets the NSData object from a UIDocument. The NSData object can get large, so I want to make sure it is fully loaded before the response starts. I would therefore like to return the value of the method from within the block itself. So something like this:
- (NSData*)getMyData {
MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
if (success) {
return doc.myResponseData; // this is to be the return for the method not the block
}
}];
}
This causes an error because the return apparently refers to the block's return.
How can I accomplish this without having to make a thread blocking wait/while loop?
Thanks.
You can't. Embrace the fact that what you're trying to do is asynchronous and add a completion block parameter to your getMyData method which is called when the inner completion handler is called. (And remove the return from the method signature):
- (void)getMyDataWithCompletion:(void(^)(NSData *data))completion {
MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
completion((success ? doc.myResponseData : nil));
}];
}
The same problem exists in swift and you can add a similar completion block:
func getMyData(completion: ((data: NSData?) -> Void) {
data = ...
completion(data)
}
The open method is asynchronous which is why you have to provide a block to be run when the open is completed. You need to copy this and make your method also receive a block of code that you will execute when the open is finished.
You should also pass through the success argument of the call you are wrapping or create an error, you need to do this so that the calling code can take the right action.
- (void)getMyDataWithCompletion:(void(^)(NSData *data, BOOL success))completion
{
MyUIDocument *doc = [[MyUIDocument alloc] initWithFileURL:fileURL];
[doc openWithCompletionHandler:^(BOOL success) {
completion(doc.myResponseData, success);
}];
}
Following Are method how to declare method with completionHandler:
Objective-C
- (void)getMyDataWithCompletionHandler:(void(^)(NSString *str))completionHandler
{
completionHandler(#"Test");
}
Swift-3
func showDatePicker(superc: UIViewController, completionHandler:#escaping (String) -> Void) {
completionHandler("Test")
}
what's is more prefered way to write multi threaded apps. I see two ways.
Implement method with GCD inside and then just simple call (myMethodA), or just implement method and then call it with GCD? Thanks in advance.
My point:
ClassA / method implementation
- (void)myMethodA
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// doSomething1
// doSomething2
});
}
- (void)myMethodB
{
// doSomething1
// doSomething2
}
ClassB / method call
{
[myClassA methodA];
// or
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[myClassA methodB];
};
}
IMHO, neither.
The preferred way should be having an object which knowns where to execute its actions:
completion_block_t completionHandler = ^(id result) { ... };
AsyncOperation* op = [AsyncOperation alloc] initWithCompletion:completionHandler];
[op start]; // executes its actions on a private execution context
Then, one can wrap those AsyncOperation objects into a convenient method:
- (void) fetchUsersWithCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:completionHandler];
[self.currentOperation start];
}
The client may only be interested in specifying where its completionHandler should be executed. The API may be enhanced as follows:
- (void) fetchUsersWithQueue:(NSOperationQueue*)handlerQueue
withCompletion:(completion_block_t)completionHandler
{
NSDictionary* params = ...;
self.currentOperation = [[HTTPOperation alloc] initWithParams:params
completion:^(id result){
// As per the documentation of HTTPOperation, the handler will be executed
// on an _unspecified_ execution context.
// Ensure to execute the client's handler on the specified operation queue:
[handlerQueue:addOperationWithBlock:^{
completionHandler(result);
}];
}];
[self.currentOperation start];
}
The latter API can be used as this:
[self fetchUsersWithQueue:[NSOperation mainQueue] completion:^(id result){
self.users = result;
[self.tableView reloadData];
}];
Personal preference. Choose whichever makes the code more readable / understandable / obvious. Also, consideration of whether the code should be possible to run on the 'current' thread or whether it should always be run on a background thread. You need to design your threading configuration, describe it and then implement with that in mind. If you're calling methods between classes like in your example then I'd generally say that any threading should be handled inside that class, not inside the calling class. But that's about distribution of knowledge.
It doesn't make much of a difference - it just depends on what you want to do.
If you want to execute the method on different queues each time, then the myMethodB system is more appropriate. If, however, you always want to run the method on the same queue, then myMethodA will save you time writing code (you only have to write the GCD code once).
How can I make sure that my function is run only on the main thread? It updates UI elements.
Is a function like this considered 'bad'?
-(void)updateSomethingOnMainThread {
if ( ![[NSThread currentThread] isEqual:[NSThread mainThread]] )
[self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO];
else {
// Do stuff on main thread
}
}
I wrote it like this to avoid having a second function, initially I had it like this:
-(void)updateSomethingOnMainThread_real {
// Do stuff on main thread
}
-(void)updateSomethingOnMainThread {
[self performSelectorOnMainThread:#selector(updateSomethingOnMainThread_real) withObject:nil waitUntilDone:NO];
}
As an alternative to ayoy's method-based GCD implementation for guaranteeing execution on the main thread, I use the following GCD-based function in my code (drawn from another answer of mine):
void runOnMainThreadWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
You can then use this helper function anywhere in your code:
runOnMainThreadWithoutDeadlocking(^{
// Do stuff that needs to be on the main thread
});
This guarantees that the actions taken in the enclosed block will always run on the main thread, no matter which thread calls this. It adds little code and is fairly explicit as to which code needs to be run on the main thread.
This is fine. You can also use GCD to execute code on the main thread.
Checkout this SO post.
GCD to perform task in main thread
I wrote this simple #define which I've been using with great success:
#define ensureInMainThread(); if (!NSThread.isMainThread) { [self performSelectorOnMainThread:_cmd withObject:nil waitUntilDone:NO]; return; }
That way your method, assuming it's parameterless, looks like this
- (void) updateTheThings {
ensureInMainThread();
[self.dog setTailWag:YES];
// etc...
Alternatively, you can use Grand Central Dispatch API, but it's not very handy:
-(void)updateSomethingOnMainThread {
void (^doStuff)(void) = ^{
// stuff to be done
};
// this check avoids possible deadlock resulting from
// calling dispatch_sync() on the same queue as current one
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (mainQueue == dispatch_get_current_queue()) {
// execute code in place
doStuff();
} else {
// dispatch doStuff() to main queue
dispatch_sync(mainQueue, doStuff);
}
}
otherwise, if synchronous call isn't needed, you can call dispatch_async() which is much simpler:
-(void)updateSomethingOnMainThread {
dispatch_async(dispatch_get_main_queue(), ^{
// do stuff
});
}
How does one determine the thread a given function call is running on?
I want to make sure a method is called on the main thread in order to update some UI elements.
Can I for example do something like this?
- (void) myMethod {
if (<current thread is not main thread>) {
[self performSelectorOnMainThread: #selector(myMethod) withObject: nil waitUntilDone: NO];
} else {
// my code here
}
}
Check out [NSThread isMainThread].
http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSThread_Class/Reference/Reference.html#//apple_ref/occ/clm/NSThread/isMainThread
I have an asset manager that needs to notify the owner it's assets are ready. I'm sending a token back for the consumer to listen to listen for a notification to avoid tighter coupling. The issue is when the assets are already loaded I need to call the loadComplete after a delay. What's the best way to do this in objective-c?
Asset Manager
-(tokenString*) loadAssetPath:(NSString*) asset {
//start asynchronous load
//or if assets ready send complete <-- issue
return nonceToken;
}
-(void)loadComplete {
[[NSNotificationCenter defaultCenter]
postNotificationName:tokenString object:self];
}
Consumer
NSString* token;
-(void) loadSomething {
if(token)
[self removeListener];
token = [[AssetManager sharedManager]
loadAssetPath:#"http://server.dev/myLargeImage.png"];
[[NSNotificationCenter defaultCenter]
addObserver:[AssetManager sharedManager]
selector:#selector(assetLoaded:) name:token];
}
-(void)assetLoader:(NSNotifcation*)aNotification {
[self removeListener];
//continue on with stuffing stuff
}
Use NSObject's performSelector function which allows it to be called after a delay.
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay
You can even use a form of this function to run it on another thread, which is useful to not blocking the main thread when doing lengthy operations (just don't muck with the UI objects in this thread).
#DavidNeiss is correct about performSelector:withObject:afterDelay:, but you almost certainly don't want an actual time delay here. At most you want to perform your selector on the next event loop, just so things are consistent for the listener. So you should make the delay 0. This differs from the normal performSelect:withObject: which will immediately perform the selector synchronously.
-(tokenString*) loadAssetPath:(NSString*) asset {
//start asynchronous load
if (<load is actually complete>) {
// -loadComplete will execute on the next event loop
[self performSelector:#selector(loadComplete) withObject:nil afterDelay:0];
}
return nonceToken;
}