NSURLConnection does not call complete across multiple Views

前端 未结 5 566
感动是毒
感动是毒 2021-01-29 03:07

Earlier today I asked the following question: iOS block being stoped when view pushed

The operation I mentioned (OP1) is actually a \"http get\" to my server, using NSUR

5条回答
  •  感动是毒
    2021-01-29 03:38

    As already mentioned in a comment in you first question: you have probably two issues:

    1. A design problem
    2. A code issue, causing the block. (but without code this is difficult to figure out).

    Lets propose a practical approach:

    Say, our singleton is some "Loader" class which performs HTTP requests. Instead of polling a property which determines the state of the network request, you should return some object which you can ask for the state, or even better where VC2 can register a completion block which gets called when the request is finished.

    An NSOperation could be "used" to represent the eventual result of the asynchronous network request. But this is a bit unwieldy - suppose we have a subclass RequestOperation:

    RequestOperation* requestOp = [[Loader sharedLoader] fetchWithURL:url];
    

    Now, "requestOp" represents your network request, including the eventual result.

    You can obtain this operation in VC1.

    You may not want to ask the shared loader about a particular operation, because it may stateless -- that is, it does not itself track the request operations. Consider, you want to use class Loader several times for starting network requests - possible in parallel. Then, which request do you mean when you ask one property of Loader which tells you something about the state of a request? (it won't work).

    So, again back to a working approach and to VC1:

    Suppose, in VC1 you obtained the RequestOperation object which is a subclass of NSOperation. Suppose, RequestOperation has a property responseBody - which is a NSData object representing the eventual response data of the request operation.

    In order to obtain the eventual response body of the request, you cannot just ask the property: the connection could possibly still running - the you would get nil or garbage, or you might block the thread. The behavior is dependent on the implementation of RequestOperation.

    The solution is as follows:

    In VC2:

    We assume, VC1 has "passed" the requestOp to VC2 (for example in prepareForSegue:sender:).

    In order to retrieve the response body in an asynchronous correct manner, you need some extra steps:

    Create a NSBlockOperation which executes a block which handles the response body, for example:

    NSBlockOperation* handlerOp = [NSBlockOperation blockOperationWithBlock:^{
        NSData* body = requestOp.responseBody;
        dispatch_async(dispatch_get_main_queue(), ^{
            self.model = body;
            [self.tableView reloadData];
        });
    }];
    

    Then, make the handlerOp dependent on the requestOp - that is, start executing handlerOp when requestOp finished:

    [handlerOP addDependency:requestOp];
    

    Add the handlerOp to a queue, in order to execute:

    [[NSOperation mainQueue] addOperation:handlerOp];
    

    This still requires you to think "asynchronously" - there is no way around this. The best is, to get used to the practical patterns and idioms.


    An alternative approach is using RXPromise (from a third party library):

    In VC1:

    requestPromise = [Loader fetchWithURL:url];
    

    Now, in VC2:

    We assume, VC1 has "passed" the requestPromise to VC2 (for example in prepareForSegue:sender:).

    For example in viewDidLoad:

    requestPromise.thenOn(dispatch_get_main_queue(), ^id(id responseBody){
        // executes on main thread!
        self.model = responseBody;
        [self.tableView reloadData];
        return nil;
    }, nil);
    

    Bonus:

    If required, you can cancel the network request at any time through sending cancel to the promise:

    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        [self.requestPromise cancel];
        self.requestPromise = nil;
    }
    

提交回复
热议问题