更改NSFetchedResultsController后,不断调用NSMangedObject子类方法NSPredicate

时间:2015-12-06 21:20:32

标签: ios core-data swift2

我一直坚持这个,找不到罪魁祸首。我将尝试尽可能多地包含有用的代码。请参阅TL; DR版本的底部。

IDEA
用户打开应用程序,您可以找到某个城市的餐馆或将其设置为查找您所在位置附近的餐馆(假设您允许该应用查看您的位置)。如果设置了城市,则NSFetchedResultsController将使用某个NSPredicate执行提取。当您点击按钮查找您附近的餐馆时,它会更改NSPredicate并再次执行获取。

设置
1)当我的UIViewController被加载时,它在NSUserDefaults的子类上调用了一个方法来获取用户的当前位置。
2)此方法告诉CLLocationManager对象开始更新用户的位置,一旦更新了位置,就会调用didUpdateLocations方法。
3)由于.desiredAccuracy设置为kCLLocationAccuracyBestdidUpdateLocations方法经常被调用,所以我设置了一个定时器来执行一个名为updateRestaurantDistance的方法,该方法对主进行提取NSManagedObjectContext并检索数据库中的所有餐馆 4)循环遍历每个餐馆并调用一种方法来设置餐馆与用户的距离 5)循环完成后,我使用更新的值保存上下文。

问题
当我启动应用程序时,一切运行正常,一旦从4)获取完成迭代完每个项目,我就可以点击获取我附近餐馆的按钮。
但是,如果4)的迭代没有完成,我点击找到我附近餐馆的按钮,迭代完成,但上下文没有保存(我设置了print个语句更新餐厅距离的方法称为无限阻塞主线程并使UI无法使用。

CODE
- UIViewController

override func viewDidLoad() {
    super.viewDidLoad()

    //configure properties
    //core data
    context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

    //fetch controller
    fetchController.delegate = self

    //default values
    user = User()
    self.user.getCurrentLocation()

 }
//method that is called when the button to filter restaurants near user is tapped
func nearMe() {

        restaurantPredicate = NSPredicate(format: "(distance < %f AND distance > %f) AND SUBQUERY(specials,$s, ($s.type LIKE %@ OR $s.type LIKE %@) AND $s.day LIKE %@).@count > 0",
            user.doubleForKey(UserData.MaxDistance.rawValue), 0.01, typeQuery!, "Combo", day)

        fetchController.fetchRequest.sortDescriptors = [distSort,nameSort,citySort]
        do {
            try fetchController.performFetch()
        } catch {
            print("Error fetching when sorting by near me")
        }
        tableView.reloadData()

    }

具有获取位置的方法的User

func getCurrentLocation() {
        //ask for access to location (if not given)
        if CLLocationManager.locationServicesEnabled() {
            locationManager.requestWhenInUseAuthorization()
        }

        //if allowed, start checking for location
        if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
        }

    }

CLLocationDelegate

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // get coordintes
        if let locValue: CLLocationCoordinate2D = manager.location?.coordinate {
            //check if timer has started
            if timer == nil {
                timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "updateRestaurantDistance", userInfo: nil, repeats: false)
            } else {
                timer?.invalidate()
                timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "updateRestaurantDistance", userInfo: nil, repeats: false)
            }

            //stop updating since we're done
            locationManager.stopUpdatingLocation()

        }
    }

更新位置方法

func updateRestaurantDistance() {
        let fetch = NSFetchRequest(entityName: "Restaurant")
        let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
        do {
            let results = try context.executeFetchRequest(fetch) as! [Restaurant]
            for result in results {
                print(results.indexOf(result))
                result.getDistance()
            }
        } catch {
            print("Error updating restaurant distances from UserClass")
        }
        do {
            try context.save()
            print("saved context")
        } catch {
            print("error saving context while updating restaurant distance: \(error)")
        }

    }

餐厅方法

//This method gets called infinitely and I have no idea why  
func getDistance() {
        print("getDistance")
        guard CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse else {
            return
        }
        self.distance = self.getDistanceFromRestaurant()

    }

    func getDistanceFromRestaurant() -> CLLocationDistance {
        let user = User()
        guard CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse else {
            return Double(0)
        }
        let longitude = user.doubleForKey(UserData.Longitute.rawValue)
        let latitude = user.doubleForKey(UserData.Latitude.rawValue)
        guard abs(longitude) + abs(latitude) > 0 else {
            return Double(0)
        }
        let loc = CLLocation(latitude: latitude, longitude: longitude)
        let selfLoc = CLLocation(latitude: Double(self.latitude), longitude: Double(self.longitude))
        return loc.distanceFromLocation(selfLoc) / 1000
    }

如果需要可以添加更多,谢谢!

编辑:似乎它到达try context.save()中的updateRestaurantDistance()并在那里开始循环(也就是说它没有到达print语句

编辑2 :TL; DR代码版本:
这会更新餐馆与用户的距离,try位于do-catch区块中,但为了简单起见已删除

func updateRestaurantDistance() {
    let fetch = NSFetchRequest(entityName: "Restaurant")
    let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext

    let results = try context.executeFetchRequest(fetch) as! [Restaurant]
    for result in results {
        result.getDistance()
    }
    try context.save()

}  

func getDistance() {
    self.distance = 10 // method to calculate distance is called, but for simplicity set to 10
}

updateResturantDistance正在运行时,我无法将NSPredicate更改为具有distance属性的任何内容,并对我的NSFetchedResultsController执行提取,即:

predicate = NSPredicate(format: "distance < %f", 10)
fetchController.fetchRequest.predicate = predicate
try fetchController.performFetch  

不起作用,但

predicate = NSPredicate(format: "city LIKE %@", "New York")
fetchController.fetchRequest.predicate = predicate
try fetchController.performFetch

作品

2 个答案:

答案 0 :(得分:0)

无论

  • 禁用导致重新加载的UI控件,直到所有内容完成

  • 保持一个Bool变量,指示您是否循环遍历这些位置。当控件被命中时,更改此变量。在循环中检查变量并在变化时中止,并从头开始而不保存。

答案 1 :(得分:0)

OH。 MY。善良。
WOW
让我们回忆一下吸气鬼和二传手。在大多数编程语言中,不像swift即Java,你必须用方法创建它们,即

private var distance = 1
getDistance() -> Int {
   return self.distance
}  

但在迅速,这是没有必要的。无论如何,我的getDistance调用了NSPredicate方法。我将方法的名称更改为getRestaurantDistance,一切都按预期运行。不要像我的名字方法那样命名为getter和setter。