我有这个功能:
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))
但是,由于某种原因,显示地点的打印语句仅为最后一次调用生成。无论我调用它多少次都是一样的,它总是只为最后一次方法调用产生。任何人都可以向我解释为什么会这样吗?
答案 0 :(得分:0)
如果您正在按照本教程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()
}
答案 1 :(得分:0)
由于fetchPlacesNearCoordinate
取消了先前的请求(异步运行),因此您必须确保在第一个请求完成之前不启动第二个请求。
最简单的方法是使用完成处理程序:
func fetchPlace(coordinate: CLLocationCoordinate2D, completionHandler: () -> ()) {
let searchedTypes = ["cafe"]
let searchRadius: Double = 150
dataProvider.fetchPlacesNearCoordinate(coordinate, radius:searchRadius, types: searchedTypes) { places in
for place: GooglePlace in places {
print(place)
completionHandler()
}
}
}
然后你可以这样做:
fetchPlace(CLLocationCoordinate2DMake(40.725203800000003, -74.011287899999999)) {
self.fetchPlace(CLLocationCoordinate2DMake(40.760920499999997, -73.988664700000001)) {
print("done with both requests")
}
}
更复杂但更通用的解决方案是将此提取包装在异步NSOperation
子类中,然后您可以将这些请求添加到专用于提取请求的串行队列中。如果你需要看看它的外观,请告诉我。
例如:
let fetchQueue: NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "com.domain.app.fetch"
queue.maxConcurrentOperationCount = 1
return queue
}()
let provider = GoogleDataProvider()
override func viewDidLoad() {
super.viewDidLoad()
let completionOperation = NSBlockOperation() {
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)
NSOperationQueue.mainQueue().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(coordinate)
}
func fetchPlace(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 `NSOperation` 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 : NSOperation {
override public var asynchronous: Bool { return true }
private let stateLock = NSLock()
private var _executing: Bool = false
override private(set) public var executing: Bool {
get {
return stateLock.withCriticalScope { _executing }
}
set {
willChangeValueForKey("isExecuting")
stateLock.withCriticalScope { _executing = newValue }
didChangeValueForKey("isExecuting")
}
}
private var _finished: Bool = false
override private(set) public var finished: Bool {
get {
return stateLock.withCriticalScope { _finished }
}
set {
willChangeValueForKey("isFinished")
stateLock.withCriticalScope { _finished = newValue }
didChangeValueForKey("isFinished")
}
}
/// Complete the operation
///
/// This will result in the appropriate KVN of isFinished and isExecuting
public func completeOperation() {
if executing {
executing = false
}
if !finished {
finished = true
}
}
override public func start() {
if cancelled {
finished = true
return
}
executing = 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) -> ())?
init(block:(AsynchronousOperation) -> ()) {
self.block = block
super.init()
}
override public func main() {
block?(self)
}
override public func completeOperation() {
block = nil
super.completeOperation()
}
}
/*
Copyright (C) 2015 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
An extension to `NSLock` to simplify executing critical code.
From Advanced NSOperations sample code in WWDC 2015 https://developer.apple.com/videos/play/wwdc2015/226/
From https://developer.apple.com/sample-code/wwdc/2015/downloads/Advanced-NSOperations.zip
*/
import Foundation
extension NSLock {
/// Perform closure within lock.
///
/// An extension to `NSLock` to simplify executing critical code.
///
/// - parameter block: The closure to be performed.
func withCriticalScope<T>(@noescape block: Void -> T) -> T {
lock()
let value = block()
unlock()
return value
}
}