4000个未聚类GMSMarker批量显示/隐藏耗时问题咨询
优化Google Maps 4000+ GMSMarker过滤性能的方案
针对你在Google Maps上处理4000个GMSMarker时的过滤耗时问题,我有几个经过实践验证的优化思路,能帮你显著提升操作效率:
1. 将数组查询改为集合查询(最立竿见影的优化)
你当前使用arrDeviceID.contains(...)来判断,而数组的contains方法是**O(n)时间复杂度,4000次循环下来就是4000*4000=1600万次操作。换成Set的话,contains是O(1)**复杂度,直接把操作量降到4000次。
实现代码:
// 提前将数组转为Set(只需做一次,比如过滤操作前) let deviceIDSet = Set(arrDeviceID) // 遍历标记时用Set查询 markers.forEach { marker in let deviceID = marker.deviceID ?? "" let shouldShow = deviceIDSet.contains(deviceID) // 后面的逻辑不变,但是查询速度快了N倍 if shouldShow { marker.map = self.googleMapView } else { marker.map = nil } }
2. 避免不必要的map属性赋值
当前代码不管Marker的当前状态,都会强制赋值mapView或nil,这会触发Google Maps多次不必要的UI重绘和布局计算。我们可以先判断当前状态,只在需要变更的时候才赋值:
let deviceIDSet = Set(arrDeviceID) markers.forEach { marker in let deviceID = marker.deviceID ?? "" let shouldShow = deviceIDSet.contains(deviceID) if shouldShow { // 只有当Marker不在地图上时,才添加到地图 if marker.map != self.googleMapView { marker.map = self.googleMapView } } else { // 只有当Marker还在地图上时,才移除 if marker.map != nil { marker.map = nil } } }
3. 维护标记的索引字典,减少遍历范围
如果你的Marker和DeviceID是一一对应的,可以提前维护一个字典[String: GMSMarker],把DeviceID作为key,对应的Marker作为value。这样过滤时,你可以:
- 先记录当前显示在地图上的所有DeviceID
- 对比目标
arrDeviceID,找出需要新增显示的和需要隐藏的Marker - 只处理这两类Marker,而不是遍历全部4000个
示例代码:
// 提前初始化并维护这个字典(比如添加Marker时同步更新) var markerDict: [String: GMSMarker] = [:] // 记录当前显示的DeviceID var currentVisibleDeviceIDs: Set<String> = [] // 过滤操作时: let targetDeviceIDs = Set(arrDeviceID) // 需要隐藏的:当前显示但不在目标列表里的ID let toHideIDs = currentVisibleDeviceIDs.subtracting(targetDeviceIDs) // 需要显示的:目标列表里但未显示的ID let toShowIDs = targetDeviceIDs.subtracting(currentVisibleDeviceIDs) // 隐藏不需要的标记 toHideIDs.forEach { id in if let marker = markerDict[id], marker.map != nil { marker.map = nil } } // 显示需要的标记 toShowIDs.forEach { id in if let marker = markerDict[id], marker.map == nil { marker.map = self.googleMapView } } // 更新当前显示的ID集合 currentVisibleDeviceIDs = targetDeviceIDs
4. 后台预处理,主线程批量更新
虽然UI操作必须在主线程,但我们可以把“筛选需要变更的Marker”这一步放到后台线程,只把最终需要更新的Marker列表拿到主线程处理,减少主线程的阻塞时间:
let deviceIDSet = Set(arrDeviceID) DispatchQueue.global().async { var toShow: [GMSMarker] = [] var toHide: [GMSMarker] = [] self.markers.forEach { marker in let deviceID = marker.deviceID ?? "" let shouldShow = deviceIDSet.contains(deviceID) if shouldShow && marker.map != self.googleMapView { toShow.append(marker) } else if !shouldShow && marker.map != nil { toHide.append(marker) } } // 主线程执行UI更新 DispatchQueue.main.async { toHide.forEach { $0.map = nil } toShow.forEach { $0.map = self.googleMapView } } }
这些方案可以组合使用,比如同时用Set查询+避免不必要赋值+后台预处理,能把你的过滤操作耗时降到原来的几十分之一甚至更低。
内容的提问来源于stack exchange,提问作者Parth Adroja
相关产品推荐
相关产品推荐

