0

I have source code as following:

+ (NSDictionary *)incrementalUpdateTask
{
    __block NSDictionary *result = nil;
    __block BOOL isFinish   = NO;
    [EQPlatformManager getIncrementTaskWithSuccess:^(NSArray *deletedList, NSArray   *updateList) {
    result      = @{@"delete":deletedList, @"update":updateList};
    isFinish    = YES;

    } failed:^(NSError *error) {
        isFinish    = YES;
    }];
    while (!isFinish) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                             beforeDate:[NSDate distantFuture]];
    }
    return result;
}

This code segment leads to exception as following:

0 CoreFoundation    ___exceptionPreprocess + 124
1 libobjc.A.dylib   objc_exception_throw + 56
2 CoreFoundation    +[NSException raise:format:]
3 Foundation    -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:] + 88
4 UIKit __prepareForCAFlush + 500
5 UIKit __beforeCACommitHandler + 24
6 CoreFoundation    ___CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
7 CoreFoundation    ___CFRunLoopDoObservers + 372
8 CoreFoundation    CFRunLoopRunSpecific + 476
9 Foundation    -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 304
10 MyAPPName    +[EQUtil incrementalUpdateTask] (EQUtil.m:2317)
11 MyAPPName    -[EQMainTaskViewController appDidBecomeActive] (EQMainTaskViewController.m:331)

This exception happens occasionally,and very hard to reproduce. Can some one help me?

Emalwb
  • 1
  • 1

2 Answers2

0

Its a mishmash mixture of synchronous and asynchronous code.You have a block and then a loop but the loop can, and will, execute before the asynchronous code has finished. You simply cannot have the loop depending upon the result of the block like that.And that while loop is just dreadful, sorry there's no other word for it, I don't mean to sound rude, but it really shows you don't know whats going on. I urge you to read up lots on asychonicity and blocks and understand both fully before doing anything else like this.

You need to get rid of the loop, move the code that is in the loop inside the block. Also you have to get rid of the return statement, you can't keep the return statement where it is, but you also cannot put it inside the block. You need to notify the calling code of the result in an asynchronous way, either by passing a block to incrementalUpdateTask, or post a notification to the calling code.

You have a very lot to learn.

Gruntcakes
  • 37,738
  • 44
  • 184
  • 378
  • Thank you very much. Method `incrementalUpdateTask` execute in the handler of UIApplicationDidBecomeActiveNotification, which means it runs in main thread. Actually, I expect *while loop* execute **before** the asynchronous code has finished. It means main thread will be blocked, until asynchronous code finished. This is just what I want. – Emalwb Nov 23 '16 at 06:41
  • You should never ever block a thread on purpose. Whatever you are trying to do your idea of how to do it is wrong. There will always be a better proper solution. – Gruntcakes Nov 23 '16 at 15:00
0

You need to change your method declaration as below with block .

- (NSDictionary *)incrementalUpdateTaskWithBlock:(void(^)(NSArray *deletedList, NSArray *updateList ,BOOL isFinish ))callback
{
    [EQPlatformManager getIncrementTaskWithSuccess:^(NSArray *deletedList, NSArray   *updateList, BOOL isFinish) {
        callback(deletedList ,updateList , YES );// pass parameter as your requirement

    } failed:^(NSError *error) {
        callback(nil ,nil , YES); // pass parameter as your requirement
    }];

}

// for get result

-(void)getResult{
    [self incrementalUpdateTaskWithBlock:^(NSArray *deletedList, NSArray *updateList, BOOL isFinish) {
        NSLog(@"deleted list : %@", deletedList);
        if (!isFinish) {
            // as per your need
           // [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
                                     //beforeDate:[NSDate distantFuture]];
        }
    }];
}
KKRocks
  • 8,222
  • 1
  • 18
  • 84