长时间潜伏,第一次海报。我对Objective-C比较陌生,所以如果我问一些相当简单的事情我会道歉。我的谷歌& stack overflow-fu让我失望了,所以我觉得有人可能会帮忙。
我有一个同步过程,例如,连续执行三个函数 - 称之为A - > B-> C,执行任务A,然后是B,然后是C.
现在,B涉及一个带有委托回调的异步进程以完成。但是B必须在执行C之前完成,所以我需要一些机制,使得在B完成之前不会触发C.我想这个问题必须有一个共同的设计模式吗?
最初天真的解决方案是 -
执行A
执行B
而(!B完成){}
执行C
......但这看起来真的很蹩脚。
我怀疑我可以通过某种阻挡来做到这一点,但对于我的生活,我无法理解。有人可以帮忙吗?
感谢任何帮助!
纪尧姆
答案 0 :(得分:4)
感谢所有的反馈 - 抱歉没有尽快回复。我现在以与建议略有不同的方式解决了这个问题:
首先,我将NSObject扩展为具有以下方法 -
#import "NSObject+LTExtensions.h"
@implementation NSObject (Testing)
- (void) performSelectorWithBlock: (SEL) selector withSemaphore:(dispatch_semaphore_t)semaphore
{
[self performSelector:selector]; // This selector should complete the semaphore
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
}
@end
这允许我通过选择器执行一个块。当块执行时,执行它的线程将等待,直到通过特定的调度信号量发出信号。
我们可以做的事情如下:
所以我们有
A
B -> Asynchronous with delegate callback
C
以下是如何实施上述内容的简单示例
-(void) methodA {
// ... do something
// Assign your semaphore (this is a dispatch_semaphore_t)
self.semaphore = dispatch_semaphore_create(0);
[self performSelectorWithBlock:@selector(methodB) withSemaphore:semaphore];
[self methodC];
}
-(void) methodB {
// ... do whatever needs to be done asynchronously
CFRunLoopRun();
}
-(void) methodBDelegateCallBack {
// This is called when B completes
// Signal completion
dispatch_semaphore_signal(self.semaphore);
CFRunLoopStop(CFRunLoopGetCurrent());
}
-(void) methodC {
...
}
工作得非常好,没有任何问题(但我是Obj C的新手,因此我的方法可能存在明显的问题。)
答案 1 :(得分:2)
此问题的另一种方法可能如下:为异步任务创建一个辅助对象,并在调用任务时复制完成块。完成异步任务后,使用委托方法调用完成块。因此,我们可以按如下顺序执行任务:
FSTask *taskA = [FSTask taskWithName:@"Task A"];
FSAsyncTask *taskB = [FSAsyncTask asyncTaskWithName:@"Task B"];
FSTask *taskC = [FSTask taskWithName:@"Task C"];
[taskA performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(@"%@", result);
[taskB performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(@"%@", result);
[taskC performTaskWithCompletionBlock:^ (NSString *result) {
NSLog(@"%@", result);
}];
}];
}];
那么这是如何实现的?好吧,看看下面的任务对象......
FSTask.m - 在主线程上同步工作......
@interface FSTask ()
@property (nonatomic, copy) NSString *name;
@end
@implementation FSTask
@synthesize name = _name;
+ (FSTask *)taskWithName:(NSString *)name
{
FSTask *task = [[FSTask alloc] init];
if (task)
{
task.name = name;
}
return task;
}
- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
NSString *message = [NSString stringWithFormat:@"%@: doing work on main thread ...", _name];
NSLog(@"%@", message);
if (block)
{
NSString *result = [NSString stringWithFormat:@"%@: result", _name];
block(result);
}
}
@end
FSAsyncTask.m - 后台线程的异步工作......
@interface FSAsyncTask ()
@property (nonatomic, copy) void (^block)(NSString *taskResult);
@property (nonatomic, copy) NSString *name;
- (void)performAsyncTask;
@end
@implementation FSAsyncTask
@synthesize block = _block;
@synthesize name = _name;
+ (FSAsyncTask *)asyncTaskWithName:(NSString *)name
{
FSAsyncTask *task = [[FSAsyncTask alloc] init];
if (task)
{
task.name = name;
}
return task;
}
- (void)performTaskWithCompletionBlock:(void (^)(NSString *taskResult))block
{
self.block = block;
// the call below could be e.g. a NSURLConnection that's being opened,
// in this case a NSURLConnectionDelegate method will return the result
// in this delegate method the completion block could be called ...
dispatch_queue_t queue = dispatch_queue_create("com.example.asynctask", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^ {
[self performAsyncTask];
});
}
#pragma mark - Private
- (void)performAsyncTask
{
for (int i = 0; i < 5; i++)
{
NSString *message = [NSString stringWithFormat:@"%d - %@: doing work on background thread ...", i, _name];
NSLog(@"%@", message);
[NSThread sleepForTimeInterval:1];
}
// this completion block might be called from your delegate methods ...
if (_block)
{
dispatch_async(dispatch_get_main_queue(), ^ {
NSString *result = [NSString stringWithFormat:@"%@: result", _name];
_block(result);
});
}
}
@end
答案 2 :(得分:1)
您可以将块属性分配给B,在调用委托方法之前,它将用于执行代码块。类似的东西:
@property(nonatomic,copy)void(^ yourBlock)(id blockParameter);
因此,在调用B的委托后,您可以调用此块并执行它。在这个区块内,你可以调用C的方法。
答案 3 :(得分:1)
我处理这个的方式是。
我在异步调用之前创建了一个NSMutableDictionary。
然后我进行异步调用。并检查我正在等待的价值
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
[AsyncCallClass asyncCall:^{
@synchronized(dictionary) {
[dictionary setValue:myValue forKey:@"result"];
}
}];
while (true){
@synchronized(dictionary){
if ([dictionary valueForKey:@"resultValue"] != nil){
break;
}
}
[NSThread sleepForTimeInterval:.25];
}
MyResultClass *result = [dictionary valueForKey:@"resultValue"];
你可以为此添加时间以阻止它成为无限循环。但这是我的解决方案。它看起来效果很好。
答案 4 :(得分:1)
以下是我用来执行此类操作的典型代码(根据您的需要调整completionBlock签名和方法名称)
typedef void (^BCompletionBlock)(void);
@interface B : NSObject <BDelegate>
@property(nonatomic, copy) BCompletionBlock completionBlock;
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock;
@end
@implementation B
-(void)doAsynchronousActionWithCompletion:(BCompletionBlock)aCompletionBlock
{
// Store the completion block for later use
self.completionBlock = aCompletionBlock;
// Then execute your asynchronous action, that will call some delegate method when done
[self doYourAsynchronousActionWithDelegate:self];
}
-(void)yourBDelegateMethodCalledWhenDone
{
// Upon your async task completion, call your completion block then
if (self.completionBlock) self.completionBlock();
}
@end
然后是一个示例用法:
-(void)doActions
{
[a doSynchronousAction];
[b doAsynchronousActionWithCompletion:^{
[c doSynchronousAction];
// A,B,C are now done
}];
}
我一直这样做“转换”使用委托方法的动作(告诉我它们什么时候完成)到使用completionBlocks的动作(有一些类为UIAlertViews,UIActionsSheets和更多的案例执行此操作)例如)它就像一个魅力。
在这种情况下,我发现使用completionBlocks要比委托机制容易得多。
答案 5 :(得分:0)
您也可以像这样在一个块中传递C ...
定义自定义块
typedef void(^myCompletion)(BOOL complete);
创建B方法
-(void)performBWithCompletionBlock:(myCompletion)complete;
{
// do your things
[self.delegate delegateCallback];
complete(YES);
}
然后创建BG /异步ABC
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // now we're on a BG queue to perform our async tasks
[self performA];
[self performBWithCompletionBlock:^(BOOL complete) {
if (complete == YES)
[self performC];
}];
});
如果你想让C在主线程上
dispatch_async(dispatch_get_main_queue(), ^{
[self performC];
});