Fat*_*tie 7 iphone multithreading nsoperation grand-central-dispatch ios
我一直在想,你能用你用GCD推出的线程取消/ cancelAllOperations/.isCancelled吗?
目前,我只使用布尔值作为标志来取消后台进程.
假设您希望在后台进行大量处理,同时保持UI响应,以便您可以捕获取消按钮(或动画显示处理器工作的动画).这是我们如何做到的......
@interface AstoundingView : UIView
{
BOOL pleaseAbandonYourEfforts;
blah
}
@implementation AstoundingView
//
// these are the foreground routines...
// begin, abandon and all-done
//
-(void)userHasClickedToBuildASpaceship
{
[YourUIStateMachine buildShip];
[self procedurallyBuildEnormousSpaceship];
}
-(void)userHasClickedToAbandonBuildingTheSpaceship
{
[YourUIStateMachine inbetween];
pleaseAbandonYourEfforts = false; // that's it!
}
-(void)attentionBGIsAllDone
{
// you get here when the process finishes, whether by completion
// or if we have asked it to cancel itself.
[self typically setNeedsDisplay, etc];
[YourUIStateMachine nothinghappening];
}
//
// these are the background routines...
// the kickoff, the wrapper, and the guts
//
// The wrapper MUST contain a "we've finished" message to home
// The guts can contain messages to home (eg, progress messages)
//
-(void)procedurallyBuildEnormousSpaceship
{
// user has clicked button to build new spaceship
pleaseAbandonYourEfforts = FALSE;
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^{ [self actuallyProcedurallyBuildInBackground]; }
);
// as an aside, it's worth noting that this does not work if you
// use the main Q rather than a global Q as shown.
// Thus, this would not work:
// dispatch_async(dispatch_get_main_queue(), ^{ ...; });
}
-(void)actuallyProcedurallyBuildInBackground
{
// we are actually in the BG here...
[self setUpHere];
// set up any variables, contexts etc you need right here
// DO NOT open any variables, contexts etc in "buildGuts"
// when you return back here after buildGuts, CLEAN UP those
// variables, contexts etc at this level.
// (using this system, you can nest as deep as you want, and the
// one CHECKER pseudocall will always take you right out.
// You can insert CHECKERs anywhere you want.)
[self buildGuts];
// Note that any time 'CHECKER' "goes off', you must fall-
// through to exactly here. This is the common fall-through point.
// So we must now tidy-up, to match setUpHere.
[self wrapUpHere];
// when you get to here, we have finished (or, the user has cancelled
// the background operation)
// Whatever technique you use,
// MAKE SURE you clean up all your variables/contexts/etc before
// abandoning the BG process.
// and then you must do this......
// we have finished. it's critical to let the foreground know NOW,
// or else it will sit there for about 4 to 6 seconds (on 4.3/4.2)
// doing nothing until it realises you are done
dispatch_sync(
dispatch_get_main_queue(),
^{[self attentionBGIsAllDone];} // would setneedsdisplay, etc
);
return;
}
-(void)buildGuts
{
// we are actually in the BG here...
// Don't open any local variables in here.
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
// to get stuff done from time to time on the UI, something like...
CHECKER
dispatch_sync(
dispatch_get_main_queue(),
^{[supportStuff pleasePostMidwayImage:
[UIImage imageWithCGImage:halfOfShip] ];}
);
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
for ( i = 1 to 10^9 )
{
CHECKER
[self blah blah];
}
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
return;
}
Run Code Online (Sandbox Code Playgroud)
和CHECKER没有什么比检查标志是真的...
#define CHECKER if ( pleaseAbandonYourEfforts == YES ) \
{NSLog(@"Amazing Interruption System Working!");return;}
Run Code Online (Sandbox Code Playgroud)
一切都很完美.
但........ 是否可以使用取消/取消AllOperations/.isCancelled与这种类型的GCD使用?
这是什么故事?干杯.
PS - 适用于使用此"六部分"背景模板的任何初学者.
请注意,正如下面BJ所述,每当你突破bg进程时...
在我的习语中,你必须分配所有变量,上下文,内存等,特别是在"setUpHere"中.你必须在"wrapUpHere"中释放它们.(如果你在BG中越走越深,这个成语继续有效.)
或者,正如BJ在他的例子中所展示的那样.(如果你使用BJ的方法,如果你更深入,要小心.)
无论使用何种方法,当您退出BG流程时,必须清理已打开的任何变量/上下文/内存.希望有时帮助某人!
BJ *_*mer 18
GCD没有内置的取消支持; 如果能够取消你的后台任务很重要,那么检查你所展示的标志是一个可以接受的解决方案.但是,您可能想要评估取消需要响应的速度; 如果这些方法中的一些调用相当短,那么您可以更频繁地检查.
您询问是否可以使用NSOperation标志来支持取消.答案是不.GCD不是基于NSOperation.事实上,在Snow Leopard中,NSOperation和NSOperationQueue被重新实施以在内部使用GCD.所以依赖是另一种方式.NSOperation是一个比GCD更高层次的构造.但是,即使你要使用NSOperation,你的取消实现也会大致相同; 你还需要self.isCancelled定期检查你是否应该放弃太空船的建造.
我对CHECKER宏实现的唯一顾虑是它实现了一个意外的return.因此,您必须小心内存泄漏.如果您在后台线程上设置了自己的NSAutoreleasePool,则需要drain在返回之前使用它.如果您alloc编辑或retain编辑了任何对象,则可能需要release在返回之前使用它们.
由于所有清理工作都需要在每次检查时进行,因此您可能需要考虑转向单一返回点.一种方法是将每个方法调用包装在一个if (pleaseAbandonYourEfforts == NO) { }块中.这可以让您在请求取消后快速进入方法的最后,并将清理保存在一个位置.另一种选择,虽然有些人可能不喜欢它,但是要进行宏使用调用goto cleanup;并cleanup:在方法结尾附近定义一个标签,在该方法中你释放需要释放的东西.有些人不喜欢goto以近乎宗教的方式使用,但我发现向这样的清理标签向前跳转通常比替代方案更清晰.如果您不喜欢它,将一切包装在一个if块中也同样适用.
编辑
我觉得有必要进一步澄清我之前关于单一回归点的陈述.使用CHECKER上面定义的宏,该-buildGuts方法可以在使用该宏的任何点返回.如果该方法存在任何本地保留对象,则必须在返回之前清除它们.例如,想象一下这种非常合理的修改-buildGuts方法:
-(void)buildGuts
{
// we are actually in the BG here...
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self recordSerialNumberUsingFormatter:formatter];
// ... etc ...
[formatter release];
return;
}
Run Code Online (Sandbox Code Playgroud)
请注意,在这种情况下,如果CHECKER宏导致我们在方法结束之前返回,则对象in formatter将不会被释放并将被泄露.虽然[self quickly wrap up in a bow]调用可以处理通过实例变量或全局指针可到达的任何对象的清理,但它不能释放仅在buildGuts方法中本地可用的对象.这就是为什么我建议goto cleanup实现,看起来像这样:
#define CHECKER if ( pleaseAbandonYourEfforts == YES ) { goto cleanup; }
-(void)buildGuts
{
// we are actually in the BG here...
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
CHECKER
[self blah blah];
CHECKER
[self blah blah];
CHECKER
[self recordSerialNumberUsingFormatter:formatter];
// ... etc ...
cleanup:
[formatter release];
return;
}
Run Code Online (Sandbox Code Playgroud)
在此实现下formatter,无论何时取消,都将始终发布.
简而言之,每当你制作一个可能导致你从方法返回的宏时,你需要非常确定在你过早返回之前,所有的内存管理都已经得到了解决.使用导致返回的宏来干净利落是很难的.