[英]swift function is only being called once
我有这个功能:
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)
}
}
}
我试图简单地调用它两次
self.fetchPlace(CLLocationCoordinate2DMake(40.725203800000003, -74.011287899999999))
self.fetchPlace(CLLocationCoordinate2DMake(40.760920499999997, -73.988664700000001))
但是,出于某种原因,显示位置的打印语句仅在最后一次调用时生成。 无论我调用多少次都是一样的,它总是只为最后一次方法调用产生。 谁能向我解释这是为什么?
因为fetchPlacesNearCoordinate
取消了前一个请求(异步运行),所以您必须确保在第一个请求完成之前不要启动第二个请求。
最简单的方法是使用完成处理程序:
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()
}
}
}
然后你可以这样做:
fetchPlace(for: CLLocationCoordinate2DMake(40.725203800000003, -74.011287899999999)) {
self.fetchPlace(for: CLLocationCoordinate2DMake(40.760920499999997, -73.988664700000001)) {
print("done with both requests")
}
}
一个更复杂但更通用的解决方案是将此提取包装在自定义的异步Operation
子类中,然后您可以将这些请求添加到专用于提取请求的串行队列中。 如果你需要看看它会是什么样子,请告诉我。
例如:
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)
}
哪里:
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()
}
}
}
}
并且:
//
// 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()
}
}
如果您遵循本教程https://www.raywenderlich.com/109888/google-maps-ios-sdk-tutorial
您可以在下面的代码中看到,如果有一个正在运行的任务,则该任务被取消并启动另一个任务。
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.