Storing values in completionHandlers - Swift

前端 未结 1 1954
花落未央
花落未央 2020-11-29 09:00

I am creating a completionHandler which returns a Dictionary but when I call this method in another class the value of it is nil.

func fetchLate         


        
相关标签:
1条回答
  • 2020-11-29 09:36

    Your question is garbled. Your sample code isn't valid, and does not show a completion handler in use.

    I suspect you misunderstand how async requests and completion handlers work. Here is the sequence.

    1. Call a function and provide a completion handler
    2. Function returns immediately, and at a time in the future beings running request
    3. Next line after completion handler runs before request even begun processing. Your app continues to run. No result available because the request has not completed yet.
    4. Async request finally finishes. original method invokes completion handler. NOW the result is available, in the code in the completion handler. The completion handler code does whatever is needed to handle the results.

    Here is a realistic example of how an async process with completion handlers might work: (You can find a working Xcode project that demonstrates this code on Github at this link)

    First a class AsyncManager that simulates downloading an image from the internet:

    class AsyncManager
    {
      static let sharedAsyncManager = AsyncManager()
    
      func asyncFetchImage(#imageName: String, completion: (image: UIImage?, status: String) -> ())
      {
        println("Entering \(__FUNCTION__)")
    
        //Simulate a network operation by waiting 3 seconds before loading an image
        let nSecDispatchTime = dispatch_time(DISPATCH_TIME_NOW, 
          Int64(3.0 * Double(NSEC_PER_SEC)))
        let queue = dispatch_get_main_queue()
        dispatch_after(nSecDispatchTime, queue)
          {
            () -> Void in
            let result = UIImage(named: imageName)
            println("Loading image in background")
            let status = result != nil ? "image loaded" : "Error loading image"
            println("About to call completion handler")
            completion(image: result, status: status)
        }
        println("Leaving \(__FUNCTION__)")
      }
    }
    

    It is a singleton. It has a static let sharedAsyncManager that you use to fetch a single instance of the AsyncManager.

    The AsyncManager provide a single method, asyncFetchImage, that takes an image name and a completion block. The function doesn't return any result, because it returns immediately, before the image load has taken place.

    The code doesn't really download the image from the internet. Instead it simply uses the GCD call dispatch_after to wait 3 seconds before loading an image and invoking the completion block.

    Now I create a ViewController class and give it a "Load Image" button. I create an IBOutlet theImageViewto a UIImageView that will display the image I'm going to load.

    I write an IBAction method for the Load Image button. Here's what that IBAction looks like:

    @IBAction func loadImage(sender: UIButton)
    {
      let theAsyncManager = AsyncManager.sharedAsyncManager
      println("about to call asyncFetchImage")
      theAsyncManager.asyncFetchImage(imageName: "wareto_blue_150x115")
        {
          (image, status) -> () in
          println("Beginning completion block")
          self.theImageView.image = image
          println("In completion block, status = \(status)")
        }
        println("After call to asyncFetchImage")
      }
    }
    

    Now, the fun part. Here's the sequence of events:

    I press the loadImage button. The IBAction method runs.

    It gets a reference to the async download manager singleton.

    It displays a message, then calls theAsyncManager.asyncFetchImage. The AsyncManager.asyncFetchImage method displays a message on entry, queues up a call to load the image 3 seconds later, displays a "leaving" message, and returns, before the image is loaded. There is nothing to return, because the image loading code hasn't run yet.

    The view controller's loadImage method displays it's "After call to asyncFetchImage" message and returns.

    A few seconds later, the code inside asyncFetchImage actually runs. It displays a message, loads the image, then invokes the completion handler.

    Here is the set of messages you get back when you run the code above:

    about to call asyncFetchImage
    Entering asyncFetchImage(imageName:completion:)
    Leaving asyncFetchImage(imageName:completion:)
    After call to asyncFetchImage
    Loading image in background
    About to call completion handler
    Beginning completion block
    In completion block, status = image loaded
    

    Note that the last line of the loadImage IBAction:

    println("After call to asyncFetchImage")
    

    displays a message before the message about loading an image is displayed. The code call to asyncFetchImage returns immediately, before any work is done. The code after the call to asyncFetchImage runs next, but the image still hasn't loaded. There's no way to return a result at this point, because the image load is hasn't even started yet.

    0 讨论(0)
提交回复
热议问题