2016-08-04 15 views
-1

と呼ばれている私は、この機能を持っている:迅速な機能は一度だけ

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) 

     } 
    } 
} 

と私は単にしかし、何らかの理由で、print文は、場所を示すために、二回

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

それを呼び出そうとしていますが最後の呼び出しのためにのみ生成されます。これは何回呼び出すにしても同じですが、最後のメソッド呼び出しのためだけに生成されます。誰が私にこの理由が説明できますか?

+3

あなたは正しい、fetchPlaces'が非同期である 'ことを理解していますか?したがって、2回目の電話が来る前に何かを印刷する時間がありません。 – matt

+0

'self.fetchPlaceOnTheWayHome'は' fetchPlace'という関数を呼び出さないので、ここではまったく別のことが起こっているかもしれません。 – matt

+0

@matt woops私はそれを修正しました。あなたはそれを私が想像しているように動作するように変更する方法を学ぶ方向で私を指摘できますか? – tryingtolearn

答えて

0

このチュートリアルhttps://www.raywenderlich.com/109888/google-maps-ios-sdk-tutorial

と一緒に、次のされた場合は、タスクがキャンセルし、別の1が開始された実行中のタスクがあるかどうかということを確認でき下回っあなたがコード内で見ることができます。

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

あなたは絶対に正しいです。それを指摘してくれてありがとう私は間違った場所で探していた人。 – tryingtolearn

+0

こんにちは、チュートリアルのこの行では、迅速2.2と私は次のエラーメッセージが表示されます: "期待値の型 '[文字列:AnyObject]'に"タイプ 'キーの値を変換できません:文字列、値:AnyObject)' コード行は: let place = GooglePlace(辞書:rawPlace、accepteTypes:types) – user2867432

0

fetchPlacesNearCoordinateは(非同期的に実行されている)事前のリクエストをキャンセルしているので、あなたは最初の1が完了するまで、あなたが第2の要求を開始していないことを確認する必要があります。

それを行うための最も簡単な方法は、完了ハンドラである:

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