Core Data执行Fetch Request时应用偶发崩溃求助(MKMapKit场景)
问题:MapView缩放时Core Data偶发EXC_BAD_ACCESS崩溃
在使用MKMapKit的mapView进行缩放操作时,应用偶发崩溃,触发点是Core Data执行Fetch Request时的EXC_BAD_ACCESS错误,且该错误并非每次都会出现。
相关代码
MapView代理方法
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { let userStatus = CoreDataUserLocationManagement().returnUsersLastKnownStatus() if (annotation is MKUserLocation) && userStatus != 3 { // 后续代码 } // 其他逻辑 }
Core Data Fetch方法
func returnUsersLastKnownStatus() -> Int16 { let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UsersLocationDetails") do { if let fetchResults = try managedObjectContext.fetch(fetchRequest) as? [UsersLocationDetails] { if fetchResults.count != 0 { let managedObject = fetchResults[0] return managedObject.lastKnownStatus } } } catch { print("Catch error retrieving last known status to Core Data") } return 3 }
崩溃日志信息
Printing description of fetchRequest: (NSFetchRequest) fetchRequest = 0x0000000283a5be20 { baseNSPersistentStoreRequest@0 = { baseNSObject@0 = { isa = NSFetchRequest } _affectedStores = 0x0000000000000000 } _groupByProperties = 0x0000000000000000 _havingPredicate = 0x0000000000000000 _additionalPrivateIvars = 0x0000000281044100 _valuesToFetch = 0x0000000000000000 _entity = 0x0000000282684dc0 { baseNSObject@0 = { isa = NSEntityDescription } _cd_rc = 12 _snapshotClass = 0x000000028208a760 _versionHashModifier = 0x0000000000000000 _versionHash = some { some = 0x0000000102faea90 { baseNSData@0 = { baseNSObject@0 = { isa = _PFEncodedData } } _aData = some { some = 0x00000002811c7e60 { baseNSData@0 = { baseNSObject@0 = { isa = _PFVMData } } _cd_rc = 1732 _length = 40944 _payload = 0x0000000102fac000 } } _byteCount = 32 _reserved = 0 } } _model = some { some = 0x00000002832a9400 { baseNSObject@0 = { isa = NSManagedObjectModel } _dataForOptimization = some { some = 0x00000002811c7e60 {...} } _optimizationHints = 0x0000000102fb5850 _additionalPrivateIvars = 0x00000002811c0120 _entities = some { some = 0x000000028239f9f0 { baseNSKnownKeysDictionary1@0 = { baseNSKnownKeysDictionary@0 = { baseNSMutableDictionary@0 = { NSDictionary = { NSObject = { isa = NSKnownKeysDictionary2 } } } } _cd_rc = 0 _count = 14 _keySearch = some { some = 0x0000000102faeb10 { baseNSKnownKeysMappingStrategy1@0 = { baseNSKnownKeysMappingStrategy@0 = { baseNSObject@0 = { isa = NSKnownKeysMappingStrategy2 } } _cd_rc = -2128839072 _reserved64 = 2 _table = 0x0000000102faeb40 _length = 14 _reserved1 = 0x0000000000000000 _keys = 0x0000000102faec28 } } } _values = {} } } } _configurations = 0x00000002811c0100 1 key/value pair _fetchRequestTemplates = 0x0000000000000000 _versionIdentifiers = 0x00000002811c0140 1 element } } _classNameForEntity = some { some = 0x0000000102fad388 { base_PFAbstractString@0 = { baseNSString@0 = { baseNSObject@0 = { isa = _PFEncodedString } } } _cd_rc = 0 _length = 20 _sourceData = some { some = 0x00000002811c7e60 {...} } } } _instanceClass = 0x00000002835b9b70 _name = some { some = 0x0000000102fad388 {...} } _rootentity = 0x0000000282684dc0 {...} _superentity = 0x0000000000000000 _subentities = 0x0000000000000000 _properties = some { some = 0x00000002835b8720 { baseNSKnownKeysDictionary1@0 = { baseNSKnownKeysDictionary@0 = { baseNSMutableDictionary@0 = { NSDictionary = { NSObject = { isa = NSKnownKeysDictionary2 } } } } _cd_rc = 0 _count = 9 _keySearch = some { some = 0x0000000102fb0bf0 { baseNSKnownKeysMappingStrategy1@0 = { baseNSKnownKeysMappingStrategy@0 = { baseNSObject@0 = { isa = NSKnownKeysMappingStrategy2 } } _cd_rc = -2128839072 _reserved64 = 2 _table = 0x0000000102fb0c20 _length = 9 _reserved1 = 0x0000000000000000 _keys = 0x0000000102fb0cd0 } } } _values = {} } } } _propertyMapping = some { some = 0x0000000102fb0bf0 {...} } _userInfo = 0x0000000000000000 _flattenedSubentities = 0x0000000281ff60d0 1 element _kvcPropertyAccessors = 0x00000002832a92c0 _modelsReferenceIDForEntity = 13 } _predicate = 0x0000000000000000 _sortDescriptors = 0x0000000000000000 _batchSize = 0 _fetchLimit = 0 _allocationSize = 0 _relationshipKeyPathsForPrefetching = 0x0000000000000000 }
Xcode额外提示
(NSPersistentStoreRequest) CoreData.NSPersistentStoreRequest = <parent failed to evaluate: invalid load address>
问题分析与解决方案
核心原因
- 线程安全问题:MKMapKit的代理方法
mapView(_:viewFor:)运行在主线程,但如果managedObjectContext是后台上下文,或者在非创建线程执行fetch操作,会导致Core Data对象访问野指针。 - 上下文生命周期问题:每次调用
CoreDataUserLocationManagement()都会创建新实例,可能导致managedObjectContext被提前释放,后续访问时出现内存错误。 - Fetch请求优化不足:未设置
fetchLimit,会一次性加载所有数据,增加内存压力和崩溃概率。
修复方案
1. 确保Core Data操作线程安全
如果使用的是私有上下文,将fetch操作放到后台线程执行,获取结果后回到主线程更新UI:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { guard annotation is MKUserLocation else { // 处理其他annotation return nil } // 异步获取用户状态 DispatchQueue.global(qos: .background).async { let userStatus = CoreDataUserLocationManagement.shared.returnUsersLastKnownStatus() DispatchQueue.main.async { if userStatus != 3 { // 更新用户位置annotation的UI } } } // 返回默认的annotationView,后续再更新 return MKAnnotationView(annotation: annotation, reuseIdentifier: "UserLocation") }
2. 单例管理Core Data操作类
将CoreDataUserLocationManagement改为单例,避免重复创建导致上下文生命周期问题:
class CoreDataUserLocationManagement { static let shared = CoreDataUserLocationManagement() private let managedObjectContext: NSManagedObjectContext private init() { // 初始化上下文,确保是线程安全的实例 let persistentContainer = NSPersistentContainer(name: "YourDataModel") persistentContainer.loadPersistentStores { _, error in if let error = error { fatalError("Core Data load failed: \(error)") } } managedObjectContext = persistentContainer.viewContext // 使用主线程上下文,或私有上下文 } func returnUsersLastKnownStatus() -> Int16 { let fetchRequest = NSFetchRequest<UsersLocationDetails>(entityName: "UsersLocationDetails") fetchRequest.fetchLimit = 1 // 只取第一条数据,优化性能 do { let fetchResults = try managedObjectContext.fetch(fetchRequest) return fetchResults.first?.lastKnownStatus ?? 3 } catch { print("获取最后已知状态失败: \(error)") return 3 } } }
3. 缓存用户状态
避免在mapView代理方法中频繁调用Core Data,缓存已获取的用户状态,减少数据库操作:
class YourMapViewController: UIViewController { private var cachedUserStatus: Int16 = 3 override func viewDidLoad() { super.viewDidLoad() // 提前获取并缓存用户状态 cachedUserStatus = CoreDataUserLocationManagement.shared.returnUsersLastKnownStatus() // 监听Core Data数据变化,更新缓存 NotificationCenter.default.addObserver(self, selector: #selector(updateUserStatusCache), name: NSNotification.Name.NSManagedObjectContextObjectsDidChange, object: CoreDataUserLocationManagement.shared.managedObjectContext) } @objc private func updateUserStatusCache() { cachedUserStatus = CoreDataUserLocationManagement.shared.returnUsersLastKnownStatus() } func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { if annotation is MKUserLocation && cachedUserStatus != 3 { // 使用缓存的状态处理UI } // 其他逻辑 } }
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

