领域因 RLMException 而崩溃:对象已被删除或无效

Realm crashes with RLMException: object has been deleted or invalidated

我有一个存储时间线的领域模型(我正在制作视频编辑应用程序)并且它经常在访问它的 RMArray 属性 时崩溃。该应用程序已经发布,我自己还没有体验过,但我的 crushlytics 经常通知我这次崩溃。这是崩溃日志:

Fatal Exception: RLMException
Object has been deleted or invalidated.

Thread : Fatal Exception: RLMException
0  CoreFoundation                 0x2614d45f __exceptionPreprocess + 126
1  libobjc.A.dylib                0x3407ec8b objc_exception_throw + 38
2  VideoEditor               0x00293919 RLMGetArray(RLMObjectBase*, unsigned int, NSString*) (RLMRealm_Private.hpp:38)
3  VideoEditor               0x0018a1b4 VideoEditor.RLMProject.setTimeLineModel (VideoEditor.RLMProject)(VideoEditor.TimeLineModel, beginWriteTransaction : Swift.Bool) -> () (RealmModels.swift:147)
4  VideoEditor               0x0025eb9c VideoEditor.VideoEditorAPI.saveProject (VideoEditor.VideoEditorAPI)(Swift.Optional<VideoEditor.IProject>, timeLine : VideoEditor.TimeLineModel, name : Swift.String, filterID : Swift.Int, image : ObjectiveC.UIImage) -> Swift.ImplicitlyUnwrappedOptional<VideoEditor.IProject> (VideoEditorAPI.swift:42)
5  VideoEditor               0x00164754 @objc VideoEditor.ProjectEditorViewController.saveProject (VideoEditor.ProjectEditorViewController)(Swift.ImplicitlyUnwrappedOptional<ObjectiveC.NSNotification>) -> () (ProjectEditorViewController.swift:514)
6  CoreFoundation                 0x26105e31 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
7  CoreFoundation                 0x260616cd _CFXNotificationPost + 1784
8  Foundation                     0x26db7dd9 -[NSNotificationCenter postNotificationName:object:userInfo:] + 72
9  UIKit                          0x296cae2d -[UIApplication _deactivateForReason:notify:] + 528
10 UIKit                          0x298d2dd7 -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:] + 1846
11 UIKit                          0x298caafd -[UIApplication workspace:didReceiveActions:] + 80
12 FrontBoardServices             0x2ca180a9 __31-[FBSSerialQueue performAsync:]_block_invoke + 12
13 CoreFoundation                 0x26113fe5 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
14 CoreFoundation                 0x261132a9 __CFRunLoopDoBlocks + 216
15 CoreFoundation                 0x26111de3 __CFRunLoopRun + 1714
16 CoreFoundation                 0x2605f3b1 CFRunLoopRunSpecific + 476
17 CoreFoundation                 0x2605f1c3 CFRunLoopRunInMode + 106
18 GraphicsServices               0x2d5bf201 GSEventRunModal + 136
19 UIKit                          0x296c943d UIApplicationMain + 1440
20 MerryVideoEditor               0x0028c88f main (main.m:16)
21 libdyld.dylib                  0x3460aaaf start + 2

这里是 RLMProject 代码:

protocol IProject{
   var name: String { get set }
   var filterID: Int { get set }
   var filterIntensity: CGFloat { get set }

   /// duration in seconds
   var duration: Int { get set }
   var dateCreated: NSDate { get }

   func setTimeLineModel(timeLine: TimeLineModel, beginWriteTransaction: Bool)

   //    should be done by ProjectImporter
   func getTimeLineModel() -> TimeLineModel

   var videoAssets: RLMArray { get }
   var soundtracks: RLMArray { get }       
}

class RLMProject: RLMObject, IProject, Printable {
   dynamic var videoAssets: RLMArray = RLMArray(objectClassName: RLMMediaAsset.className())
   dynamic var soundtracks: RLMArray = RLMArray(objectClassName: RLMMediaAsset.className())

   dynamic var name: String = ""

   dynamic var filterID: Int = 0
   dynamic var filterIntensity: CGFloat = 1

   dynamic var duration: Int = 0
   dynamic var dateCreated: NSDate = NSDate()

   dynamic var idValue: Int = 0

   func setTimeLineModel(timeLine: TimeLineModel, beginWriteTransaction: Bool = true) {
      func updateArray(array: RLMArray, withAssetsArray assetsArray: [MediaAsset], type: MediaType){
         array.removeAllObjects()
         for asset in assetsArray{
            let model = RLMMediaAsset()
            model.setMediaAsset(asset)
            model.setType(type)
            array.addObject(model)
            RLMRealm.defaultRealm().addObject(model)
         }
      }
      if beginWriteTransaction { RLMRealm.defaultRealm().beginWriteTransaction() }

      if videoAssets.invalidated { videoAssets = RLMArray(objectClassName: RLMMediaAsset.className()) }
      if soundtracks.invalidated { soundtracks = RLMArray(objectClassName: RLMMediaAsset.className()) }

      updateArray(videoAssets, withAssetsArray: timeLine.videoAssets, .Video)
      updateArray(soundtracks, withAssetsArray: timeLine.soundtracks, .Soundtrack)
      duration = Int(CMTimeGetSeconds(timeLine.totalDuration))

      dateCreated = NSDate()
      if beginWriteTransaction { RLMRealm.defaultRealm().commitWriteTransaction() }
   }

   func getTimeLineModel() -> TimeLineModel {
      let timeLine = TimeLineModel()
      timeLine.videoAssets = videoAssets.map { ([=11=] as RLMMediaAsset).getMediaAsset() }
      timeLine.soundtracks = soundtracks.map { ([=11=] as RLMMediaAsset).getMediaAsset() }

      return timeLine
   }
}

extension RLMArray {   
    func map<U>(transform: (RLMObject) -> U) -> [U]{
        var array: [U] = []
        for object in self{
            array.append(transform(object))
        }
        return array
    }
}

有人知道我的代码有什么问题吗?

RLMProject 本身失效时,将无法检查 videoAssets.invalidatedsoundtracks.invalidated,这就是为什么您的堆栈跟踪显示正在处理未捕获的异常扔进 RLMGetArray.

这意味着在您调用 setTimeLineModel 之前对象已从领域中删除。请在您的代码中查找 realm.deleteObject(_:) 以查看可能发生这种情况的位置。

最后,一些让您的代码更安全、更简单的通用技巧:

您应该使用 realm 属性,而不是 RLMObject 中的任何地方 RLMRealm.defaultRealm()。这样,如果您决定在某个时候更改领域的位置,您 RLMObject 中的代码将继续工作。

此外,您可以使用 Swift 的免费 map 功能,而不是在 RLMArray 上创建扩展来添加地图。

map(videoAssets) { ([=10=] as RLMMediaAsset).getMediaAsset() }