简体   繁体   English

swift 函数只被调用一次

[英]swift function is only being called once

I have this function:我有这个功能:

func fetchPlace(coordinate: CLLocationCoordinate2D) {

    let searchedTypes = ["cafe"]
    let searchRadius: Double = 150

    dataProvider.fetchPlacesNearCoordinate(coordinate, radius:searchRadius, types: searchedTypes) { places in
        for place: GooglePlace in places {

            print(place)

        }
    }
}

and I am attempting to simply call it twice我试图简单地调用它两次

self.fetchPlace(CLLocationCoordinate2DMake(40.725203800000003, -74.011287899999999))
self.fetchPlace(CLLocationCoordinate2DMake(40.760920499999997, -73.988664700000001))

However, for some reason, the print statements to show places are only produced for the last call.但是,出于某种原因,显示位置的打印语句仅在最后一次调用时生成。 This is the same no matter how many times I call it, it always only produces for the last method call.无论我调用多少次都是一样的,它总是只为最后一次方法调用产生。 Can anyone explain to me why this is?谁能向我解释这是为什么?

Because fetchPlacesNearCoordinate cancels the prior request (which is running asynchronously), you have to make sure you don't initiate the second request until the first one is done.因为fetchPlacesNearCoordinate取消了前一个请求(异步运行),所以您必须确保在第一个请求完成之前不要启动第二个请求。

The simplest way to do that is with a completion handler:最简单的方法是使用完成处理程序:

func fetchPlace(for coordinate: CLLocationCoordinate2D, completionHandler: @escaping () -> Void) {
    let searchedTypes = ["cafe"]
    let searchRadius: Double = 150

    dataProvider.fetchPlacesNearCoordinate(coordinate, radius: searchRadius, types: searchedTypes) { places in
        for place in places {
            print(place)
            completionHandler()
        }
    }
}

And then you can do:然后你可以这样做:

fetchPlace(for: CLLocationCoordinate2DMake(40.725203800000003, -74.011287899999999)) {
    self.fetchPlace(for: CLLocationCoordinate2DMake(40.760920499999997, -73.988664700000001)) {
        print("done with both requests")
    }
}

A more complicated, yet more generalized solution would be to wrap this fetch in a custom, asynchronous, Operation subclass, and then you could add these requests to a serial queue dedicated for fetch requests.一个更复杂但更通用的解决方案是将此提取包装在自定义的异步Operation子类中,然后您可以将这些请求添加到专用于提取请求的串行队列中。 If you need to see what that might look like, let me know.如果你需要看看它会是什么样子,请告诉我。

For example:例如:

let fetchQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.name = Bundle.main.bundleIdentifier! + ".fetch"
    queue.maxConcurrentOperationCount = 1
    return queue
}()

let provider = GoogleDataProvider()

override func viewDidLoad() {
    super.viewDidLoad()

    let completionOperation = BlockOperation {
        print("done with both requests")
    }

    let coordinate1 = CLLocationCoordinate2DMake(40.725203800000003, -74.011287899999999)
    let operation1 = FetchOperation(provider: provider, coordinate: coordinate1)
    completionOperation.addDependency(operation1)
    fetchQueue.addOperation(operation1)

    let coordinate2 = CLLocationCoordinate2DMake(40.760920499999997, -73.988664700000001)
    let operation2 = FetchOperation(provider: provider, coordinate: coordinate2)
    completionOperation.addDependency(operation2)
    fetchQueue.addOperation(operation2)

    OperationQueue.main.addOperation(completionOperation)
}

Where:哪里:

class FetchOperation: AsynchronousOperation {
    let provider: GoogleDataProvider
    let coordinate: CLLocationCoordinate2D

    init(provider: GoogleDataProvider, coordinate: CLLocationCoordinate2D) {
        self.provider = provider
        self.coordinate = coordinate
    }

    override func main() {
        fetchPlace(for: coordinate)
    }

    func fetchPlace(for coordinate: CLLocationCoordinate2D) {
        let searchedTypes = ["cafe"]
        let searchRadius: Double = 150

        provider.fetchPlacesNearCoordinate(coordinate, radius:searchRadius, types: searchedTypes) { places in
            for place: GooglePlace in places {
                print(place)
                self.completeOperation()
            }
        }
    }
}

And:并且:

//
//  AsynchronousOperation.swift
//
//  Created by Robert Ryan on 9/20/14.
//  Copyright (c) 2014 Robert Ryan. All rights reserved.
//

import Foundation

/// Asynchronous Operation base class
///
/// This class performs all of the necessary KVN of `isFinished` and
/// `isExecuting` for a concurrent `Operation` subclass. So, to developer
/// a concurrent NSOperation subclass, you instead subclass this class which:
///
/// - must override `main()` with the tasks that initiate the asynchronous task;
///
/// - must call `completeOperation()` function when the asynchronous task is done;
///
/// - optionally, periodically check `self.cancelled` status, performing any clean-up
///   necessary and then ensuring that `completeOperation()` is called; or
///   override `cancel` method, calling `super.cancel()` and then cleaning-up
///   and ensuring `completeOperation()` is called.

public class AsynchronousOperation : Operation {

    override public var isAsynchronous: Bool { return true }

    private let stateLock = NSLock()

    private var _executing: Bool = false
    override private(set) public var isExecuting: Bool {
        get {
            stateLock.withCriticalScope { _executing }
        }
        set {
            willChangeValue(forKey: "isExecuting")
            stateLock.withCriticalScope { _executing = newValue }
            didChangeValue(forKey: "isExecuting")
        }
    }

    private var _finished: Bool = false
    override private(set) public var isFinished: Bool {
        get {
            stateLock.withCriticalScope { _finished }
        }
        set {
            willChangeValue(forKey: "isFinished")
            stateLock.withCriticalScope { _finished = newValue }
            didChangeValue(forKey: "isFinished")
        }
    }

    /// Complete the operation
    ///
    /// This will result in the appropriate KVN of isFinished and isExecuting

    public func completeOperation() {
        if isExecuting {
            isExecuting = false
        }

        if !isFinished {
            isFinished = true
        }
    }

    override public func start() {
        if isCancelled {
            isFinished = true
            return
        }

        isExecuting = true

        main()
    }

    override public func main() {
        fatalError("subclasses must override `main`")
    }
}

/// Asynchronous Operation base class
///
/// This class lets you perform asynchronous block operation. Make sure that the
/// the provided `block` calls `completeOperation`, or else this operation will
/// never finish.

public class AsynchronousBlockOperation : AsynchronousOperation {
    private var block: ((AsynchronousOperation) -> Void)?

    init(block: @escaping (AsynchronousOperation) -> Void) {
        self.block = block
        super.init()
    }

    override public func main() {
        block?(self)
    }

    override public func completeOperation() {
        block = nil

        super.completeOperation()
    }
}

extension NSLock {

    /// Perform closure within lock.
    ///
    /// An extension to `NSLock` to simplify executing critical code.
    ///
    /// Adapted from Advanced NSOperations sample code in WWDC 2015's [Advanced NSOperations](https://developer.apple.com/videos/play/wwdc2015/226/).
    /// Source available at https://developer.apple.com/sample-code/wwdc/2015/downloads/Advanced-NSOperations.zip
    /// (FWIW, overall the source is very outdated, and has some dubious implementation details, but this
    /// particular method is very useful for simple use of locks for synchronization.)
    ///
    /// - parameter block: The closure to be performed.

    func withCriticalScope<T>(block: () throws -> T) rethrows -> T {
        lock()
        defer { unlock() }
        return try block()
    }
}

If you were following along with this tutorial https://www.raywenderlich.com/109888/google-maps-ios-sdk-tutorial如果您遵循本教程https://www.raywenderlich.com/109888/google-maps-ios-sdk-tutorial

you can see in the code below you can see that if there is a running tasking that task is canceled and another one starts.您可以在下面的代码中看到,如果有一个正在运行的任务,则该任务被取消并启动另一个任务。

GoogleDataProvider.swift GoogleDataProvider.swift

var placesTask: NSURLSessionDataTask?
var session: NSURLSession {
  return NSURLSession.sharedSession()
}

func fetchPlacesNearCoordinate(coordinate: CLLocationCoordinate2D, radius: Double, types:[String], completion: (([GooglePlace]) -> Void)) -> (){
    var urlString = "http://localhost:10000/maps/api/place/nearbysearch/json?location=\(coordinate.latitude),\(coordinate.longitude)&radius=\(radius)&rankby=prominence&sensor=true"
    let typesString = types.count > 0 ? types.joinWithSeparator("|") : "food"
    urlString += "&types=\(typesString)"
    urlString = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!

    //HERE!
    if let task = placesTask where task.taskIdentifier > 0 && task.state == .Running {
      task.cancel()
    }

    UIApplication.sharedApplication().networkActivityIndicatorVisible = true
    placesTask = session.dataTaskWithURL(NSURL(string: urlString)!) {data, response, error in
      UIApplication.sharedApplication().networkActivityIndicatorVisible = false
      var placesArray = [GooglePlace]()
      if let aData = data {
        let json = JSON(data:aData, options:NSJSONReadingOptions.MutableContainers, error:nil)
        if let results = json["results"].arrayObject as? [[String : AnyObject]] {
          for rawPlace in results {
            let place = GooglePlace(dictionary: rawPlace, acceptedTypes: types)
            placesArray.append(place)
            if let reference = place.photoReference {
              self.fetchPhotoFromReference(reference) { image in
                place.photo = image
              }
            }
          }
        }
      }
      dispatch_async(dispatch_get_main_queue()) {
        completion(placesArray)
      }
    }
    placesTask?.resume()
  } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM