数组在被枚举时发生了变异 - 斯威夫特

jsk*_*dd3 2 xcode ios swift xcode6

func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        if (self.busStops.count > 0) {
            if (mapView.camera!.altitude <= 1000) {
                for (var i = 0; i < self.busStops.count; i++) {
                    if (MKMapRectContainsPoint(mapView.visibleMapRect, MKMapPointForCoordinate(self.busStops[i].position))) {
                        let stop = BusAnno()
                        stop.setCoordinate(self.busStops[i].position)
                        stop.type = "stop"
                        stop.title = self.busStops[i].name
                        stop.subtitle = self.busStops[i].street
                        self.activeStops.append(stop)
                    }
                }
                dispatch_async(dispatch_get_main_queue()) {
                    self.mapView.addAnnotations(self.activeStops)
                }
            } else if (self.activeStops.count > 0) {
                mapView.removeAnnotations(self.activeStops)
                self.activeStops = []
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码目前给了我:

Terminating app due to uncaught exception 'NSGenericException',   
reason: '*** Collection <__NSArrayM: 0x1775edd0> was mutated while being enumerated.'
Run Code Online (Sandbox Code Playgroud)

发生这种情况的原因是,如果用户在应用程序仍然添加公交车站时快速缩小,则会提交"被枚举时发生变异"错误.问题是,我不知道如何解决这个问题,我基本上需要检查应用程序是否已经完成添加公交车站,然后再删除它们.

此代码的目的是在放大小于1000米时向地图添加公交车站,然后在高于此高度时移除公交车站而不会出现此错误.

Alf*_*sen 6

该问题涉及线程安全.您的busStops数组正在被2个线程同时修改.

因此,您需要同步对它的访问,即确保您对busStops阵列的更新是连续发生的(一个接一个),而不是同时发生(同时发生).一种方法是将该数组的所有修改分配给您创建的串行队列.

dispatch_get_global_queue将上述逻辑分派给全局共享并发队列.而不是使用全局队列,创建自己的队列并将其存储在类的实例变量中.

_queue = dispatch_queue_create("com.app.serialQueue", DISPATCH_QUEUE_SERIAL);
Run Code Online (Sandbox Code Playgroud)

然后根据需要向其发送工作:

dispatch_async(_queue, ^{

    // Work, i.e. modifications to busStops array

});
Run Code Online (Sandbox Code Playgroud)

如果您希望获得更多细微差别,可以将队列设置为并发队列,并dispatch_async用于对busStops阵列的所有读取,并dispatch_barrier_async用于所有写入.后者实质上使队列暂时表现为串行队列.


fis*_*ear 5

我担心"被点名时变异"并不是你最大的问题.您也快速连续启动新线程,因为mapView:regionDidChange:可以在缩放/平移期间快速连续调用.从课程参考:

只要当前显示的地图区域发生变化,就会调用此方法.在滚动期间,可以多次调用此方法以报告对地图位置的更新.

因此,您还要多次向地图添加相同的注释.

您的算法应该类似于以下内容.请注意,这只是一个草图,它不处理删除,它可能甚至不是有效的Swift,我从来没有用语言编程:

func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
    if (isUpdating) {
        return;
    isUpdating = true;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
        var currentRect;
        do {
            currentRect = mapView.visibleMapRect;    
            var toAdd = [];            
            for (var i = 0; i < self.busStops.count; i++) {
                if (!self.busStops[i].isOnMap && MKMapRectContainsPoint(currentRect, MKMapPointForCoordinate(self.busStops[i].position))) {
                    // create stop
                    toAdd.append(stop);
                    self.busStops[i].isOnMap = true;
                }
            }
            dispatch_async(dispatch_get_main_queue()) {
                self.mapView.addAnnotations(toAdd)
            }
        } while (mapView.visibleMapRect != currentRect);
        isUpdating = false;
    }
}
Run Code Online (Sandbox Code Playgroud)