簡體   English   中英

swift 函數只被調用一次

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM