[英]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.