swift函数只被调用一次

时间:2016-08-04 19:44:17

标签: ios swift xcode 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)

        }
    }
}

我试图简单地叫它两次

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

但是,由于某种原因,显示地点的打印语句仅为最后一次调用生成。无论我调用它多少次都是一样的,它总是只为最后一次方法调用产生。任何人都可以向我解释为什么会这样吗?

2 个答案:

答案 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
    }
}