i need to make this methods:
1) find and remove matches
2) fill empty cells
3) do 1 and 2 while no matches found
4) only when no matches found - find reshuffle method
How i do this :
[self launchAsyncCheckAndFill:^{
[self isReshuffleNeeded];
}];
-(void)launchAsyncCheckAndFill:(void(^)(void))completionHandler {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelectorOnMainThread:#selector(check) withObject:nil waitUntilDone:true];
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler();
});
});
}
- (void) check {
[self performSelector:#selector(fillEmptyCells) withObject:nil afterDelay:0.4];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
if ([self checkMapForMatchesWithFindMatchesMode:NO isCopyVector:NO]) {
[self check];
}
});
}
First of all i'm remove matches, then i go this methods : first of all i go launchAsyncCheckAndFill method, that on call back must start reshuffle method.
launchAsyncCheckAndFill must send callback only when check method is done. Check method is done in loop, this method must always fillEmptyCells, then checkmatches and while matches not found, only then must work callback on launchAsyncCheckAndFill .
But now behavior, that in loop always removing match, but method on reshuffle works after first remove and fill.
btw time 0.4 , 0.5 it's for animation, for this time figures moves and changes position. unfortunately in cocos i can only use
[elem.sprite runAction:[CCMoveTo actionWithDuration:0.4f position:elemPos1]];
that put position only after delay.
UPDATE:
[self launchAsyncCheckAndFills];
[self isReshuffleNeeded];
-(void)launchAsyncCheckAndFills {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
[self check];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_release(group);
}
- (void) check {
[self performSelector:#selector(fillEmptyCells) withObject:nil afterDelay:0.4];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
if ([self checkMapForMatchesWithFindMatchesMode:NO isCopyVector:NO]) {
[self check];
}
});
}
Related
Below code snippet is in objective C
__weak MyView *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.activityIndicatorView stopAnimating];
[weakSelf.activityIndicatorView removeFromSuperview];
weakSelf.activityIndicatorView = nil;
});
Will the weakSelf always available/valid since it is inside the main
queue?
Do we need to declare strongSelf only when block is other
than the main queue?
Your code snippet is too small to answer on your questions fully.
weakSelf can be both nil or non-nil. The keyword weak means that variable weakSelf can become nil in some cases. For example, if your controller has the following property:
#property (retain) MyView* myView;
In some cases you dismiss this controller and after that you call the method f for myView:
[self dismissViewControllerAnimated:YES completion:^{
[self.myView f];
}];
Code of the method f is based on code snipped you provided in this question:
-(void)f {
[self removeFromSuperview];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
__weak MyView *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.activityIndicatorView stopAnimating];
[weakSelf.activityIndicatorView removeFromSuperview];
weakSelf.activityIndicatorView = nil;
});
});
}
I guess you will see in debugger that the weakSelf will be nil when you will try to call stopAnimating for activityIndicatorView. And I guess you can easily reproduce the situation when weakSelf will be not cleared. It means that the answer on your first question is "No, the weakSelf will be not always available/valid and the main thread does not protect you from nil in this variable"
You need to use strongSelf (__strong instead of __weak) if you don't want to loose a reference to variable inside block. For example, if in the class MyView there is a method log that logs some debug information:
-(void)log {
NSLog(#"LOG");
}
And if you want to log information always after the code in your code snippet will called use the following version of the method f:
-(void)f {
[self removeFromSuperview];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
__strong MyView *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.activityIndicatorView stopAnimating];
[weakSelf.activityIndicatorView removeFromSuperview];
weakSelf.activityIndicatorView = nil;
[weakSelf log];
});
});
}
So, the answer, on your second question is "No, you need to use __strong based on your application, the block can be completed in different threads".
I am currently developing an application that runs its own event loop. If i use dispatch async when not enqueued from another dispatch, its block runs correctly.
Example:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"This should appear!");
});
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
}
If I run this, the log will be called as expected.
But let's say I put a wrap around that dispatch, like this:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"This should appear!");
});
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
}
});
If i run this code, the log will not be executed. I can imagine that since extEventMatchingMask: is being called inside a enqueued block, it will not be able to handle future blocks that are dispatched. But the weird thing is that if I use performSelector: it gets executed even inside a enqueued block.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"This should appear!");
});
[self performSelector:#selector(logAppear) withObject:nil afterDelay:2];
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
}
});
-(void) logAppear {
NSLog(#"This should appear!");
}
If I run this example the method logAppear will be called as expected.
So, in short I wanted to ask why the second case doesn't work, and how (if possible) can I run a event loop inside an enqueued block that is able to run dispatch blocks.
After some experimenting I realized that what I needed to do was to make sure that that event loop was executed outside any enqueued block. We can do this by putting the loop inside a method and make sure it is called by a performSelector.
Example:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(#"This should appear!");
});
[self performSelector:#selector(logAppear) withObject:nil afterDelay:2];
[self performSelector:#selector(mainLoop) withObject:nil afterDelay:0];
});
-(void) mainLoop {
for (;;)
{
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
[NSApp sendEvent:event];
}
}
-(void) logAppear {
NSLog(#"This should appear!");
}
By running this example we now get both logs as expected
I'm looking for something that uses XCTextExpectation in the setup or teardown methods. In Objective-C, it would look something like this:
- (void)setUp {
XCTestExpectation *getUserAsyncComplete = [self expectationWithDescription:#"Get Request Attempted and Finished"];
[Connection loginWithEmail:#"some#email.com" onSuccess:^void(^) {
[getUserAsyncComplete fulfill];
} onError:nil;
[self waitForExpectationsWithTimeout:[self.timeLimit doubleValue] handler:^(NSError *error) {
if (error != nil) {
XCTFail(#"Failure: user retrieval exceeded %f seconds.", [self.timeLimit doubleValue]);
}
}];
}
I've tried this code and it doesn't seem to be working; either that could be because Xcode 6 is still in beta, or it's not supported. Even if there is a solution in Swift, that would be greatly appreciated.
You mistyped the method name, this seems to work just fine (Xcode 6, beta 2)
- (void)setUp {
[super setUp];
XCTestExpectation *exp = [self expectationWithDescription:#"Login"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(29 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[exp fulfill];
});
[self waitForExpectationsWithTimeout:30 handler:^(NSError *error) {
// handle failure
}];
}
- (void)testExample {
XCTFail(#"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
got this code:
[self setQRCodeScannerMode:false];
[self.activityIndicator startAnimating];
TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
if ([[TBXML elementName:tbxmlDocument.rootXMLElement] isEqualToString:#"xxxxxx"]){
[self setQRCodeScannerMode:true];
} else {
[self setQRCodeScannerMode:true];
}
};
The thing is that when I set my scan mode to true inside the method [setQRCodeScannerMode] I'm stopping the activity indicator.
But surprise!!! the activity indicator is still working and messing with my view after some seconds.
What can I do?
You need to stop the activity indicator in your completion block:
[self setQRCodeScannerMode:false];
[self.activityIndicator startAnimating];
TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
if ([[TBXML elementName:tbxmlDocument.rootXMLElement] isEqualToString:#"xxxxxx"]){
[self setQRCodeScannerMode:true];
} else {
[self setQRCodeScannerMode:true];
}
[self.activityIndicator stopAnimating];
};
This assumes the completion block is called on the main thread. If there is no guarantee that the completion block is called on the main thread you can do this:
[self setQRCodeScannerMode:false];
[self.activityIndicator startAnimating];
TBXMLSuccessBlock successBlock = ^(TBXML *tbxmlDocument) {
if ([[TBXML elementName:tbxmlDocument.rootXMLElement] isEqualToString:#"xxxxxx"]){
[self setQRCodeScannerMode:true];
} else {
[self setQRCodeScannerMode:true];
}
if ([NSThread isMainThread]) {
[self.activityIndicator stopAnimating];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self.activityIndicator stopAnimating];
});
}
};
You are supposed to call [self.activityIndicator stopAnimating]; after you are finished with it, if thats not working for you, try this!
[self.activityIndicator performSelector:#selector(stopAnimating) withObject:nil afterDelay:0.1];
Hope that Helps!
One thing that you can do is working with the indicator on a different thread i.e. not on the Main Thread. You can use NSOperationQueue and NSInvocationOperation
simply add this code after the block
[self.activityIndicator stopAnimating];
I found that as predicted when I was writing an image to file that my UI was blocked for the duration, which was not acceptable. When I write the image to file I then post an NS Notification so that I can do some other specific jobs related to that completion. Original working but UI blocking code:
-(void)saveImageToFile {
NSString *imagePath = [self photoFilePath];
BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];
if (jpgData) {
[[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self];
}
To avoid the UI blocking I have put the writeToFile: into a Grand Central Dispatch queue so it runs as a concurrent thread. But when the write is completed and the thread is done, I want to post an NSNotification. I cannot as the code is shown here because it is in a background thread. But that is the functionality I want to accomplish, realizing this is not workable code:
-(void)saveImageToFile {
NSString *imagePath = [self photoFilePath];
// execute save to disk as a background thread
dispatch_queue_t myQueue = dispatch_queue_create("com.wilddogapps.myqueue", 0);
dispatch_async(myQueue, ^{
BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];
dispatch_async(dispatch_get_main_queue(), ^{
if (jpgData) {
[[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self];
}
});
});
}
What is the correct mechanism here to post this notification to gain the functionality I want ?
A couple possibilities here.
1)
How about [NSObject performSelectorOnMainThread: ...] ?
E.G.
-(void) doNotification: (id) thingToPassAlong
{
[[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:thingToPassAlong];
}
-(void)saveImageToFile {
NSString *imagePath = [self photoFilePath];
// execute save to disk as a background thread
dispatch_queue_t myQueue = dispatch_queue_create("com.wilddogapps.myqueue", 0);
dispatch_async(myQueue, ^{
BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];
dispatch_async(dispatch_get_main_queue(), ^{
if (jpgData) {
[self performSelectorOnMainThread: #selector(doNotification:) withObject: self waitUntilDone: YES];
}
});
});
}
More details at http://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/instm/NSObject/performSelectorOnMainThread:withObject:waitUntilDone:
or 2)
Completion Callbacks
as seen at How can I be notified when a dispatch_async task is complete?
-(void)saveImageToFile {
NSString *imagePath = [self photoFilePath];
// execute save to disk as a background thread
dispatch_queue_t myQueue = dispatch_queue_create("com.wilddogapps.myqueue", 0);
dispatch_async(myQueue, ^{
BOOL jpgData = [UIImageJPEGRepresentation([[self captureManager] stillImage], 0.5) writeToFile:imagePath atomically:YES];
dispatch_async(dispatch_get_main_queue(), ^{
if (jpgData) {
[[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self];
}
});
});
}
That is already correct. However why do you need to use notification if you already dispatch_get_main_queue()?
Just use
dispatch_async(dispatch_get_main_queue(), ^{
if (jpgData) {
//Do whatever you want with the image here
}
});
Anyway your original solution is fine. It's not blocking. Basically it'll save the file at other thread and once it's done it'll do what it takes.
What ever you do will be done on the same thread with the thread that call [[NSNotificationCenter defaultCenter] postNotificationName:kImageSavedSuccessfully object:self]; namely main thread.