Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
444 views
in Technique[技术] by (71.8m points)

ios - Realm crashes with RLMException: object has been deleted or invalidated

I have a realm model that stores time line (i am making video editing app) and quite frequently it crushes on accessing it's RMArray property. The app is already shipped and I haven't experienced it myself but my crushlytics notifies me about this crash quite oftenly. Here is the crash log:

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

Here is the RLMProject code:

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 { ($0 as RLMMediaAsset).getMediaAsset() }
      timeLine.soundtracks = soundtracks.map { ($0 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
    }
}

Does anybody has an idea what is wrong with my code?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

When the RLMProject itself is invalidated, it won't be possible to check for videoAssets.invalidated or soundtracks.invalidated, which is why your stack trace shows that an uncaught exception is being thrown in RLMGetArray.

This implies that the object has been deleted from the realm before you call setTimeLineModel. Please look for realm.deleteObject(_:) in your code to see where this might be happening.

Finally, a few general tips to make your code a little safer and simpler:

Instead of RLMRealm.defaultRealm() everywhere inside your RLMObject, you should use its realm property. This way, if you decide to change the location of your realm at some point, your code inside your RLMObject will continue to work.

Also, rather than create an extension on RLMArray to add map, you could use Swift's free map function.

map(videoAssets) { ($0 as RLMMediaAsset).getMediaAsset() }

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...