Realm对象已被删除或无效化。

72

当我启动我的应用程序时,我执行API调用以查看是否有新数据可用。数据存储在我的本地Realm数据库中,其中一些数据显示在初始表视图控制器中。

一旦API调用完成,我会检查是否满足某些条件,需要我从数据库中删除大量之前的数据,然后创建新对象。但是,当我删除旧数据时,我的应用程序会崩溃,并出现以下异常:

2015-08-06 11:56:32.057 MSUapp[19754:172864] *** Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'
*** First throw call stack:
(
0   CoreFoundation                      0x000000010660cc65 __exceptionPreprocess + 165
1   libobjc.A.dylib                     0x00000001083bdbb7 objc_exception_throw + 45
2   Realm                               0x0000000105b78e95 _ZL17RLMVerifyAttachedP13RLMObjectBase + 85
3   Realm                               0x0000000105b7878d _ZL10RLMGetLinkP13RLMObjectBasemP8NSString + 29
4   Realm                               0x0000000105b7c23e ___ZL17RLMAccessorGetterP11RLMProperty15RLMAccessorCodeP8NSString_block_invoke_12 + 46
5   MSUapp                              0x0000000105764867 _TFFC6MSUapp29FavoriteLeaguesViewController18generateLeagueListFS0_FT_T_U_FTCS_6LeagueS1__Sb + 39
6   MSUapp                              0x00000001057648eb _TTRXFo_oC6MSUapp6LeagueoS0__dSb_XFo_iS0_iS0__dSb_ + 27
7   libswiftCore.dylib                  0x0000000108674ae2 _TFSs14_insertionSortUSs21MutableCollectionType_USs13GeneratorType__Ss22BidirectionalIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible____FTRQ_GVSs5RangeQQ_5Index_RFTQQQ_9Generator7ElementS7__Sb_T_ + 1570
8   libswiftCore.dylib                  0x0000000108676682 _TFSs14_introSortImplUSs21MutableCollectionType_USs13GeneratorType__Ss21RandomAccessIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible_Ss16SignedNumberType_S3_____FTRQ_GVSs5RangeQQ_5Index_RFTQQQ_9Generator7ElementS8__SbSi_T_ + 1250
9   libswiftCore.dylib                  0x0000000108676172 _TFSs10_introSortUSs21MutableCollectionType_USs13GeneratorType__Ss21RandomAccessIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible_Ss16SignedNumberType_S3_____FTRQ_GVSs5RangeQQ_5Index_FTQQQ_9Generator7ElementS8__Sb_T_ + 1058
10  libswiftCore.dylib                  0x00000001085ec947 _TFSs4sortUSs21MutableCollectionType_USs13GeneratorType__Ss21RandomAccessIndexType_Ss18_SignedIntegerType_Ss33_BuiltinIntegerLiteralConvertible_Ss16SignedNumberType_S3_____FTRQ_FTQQQ_9Generator7ElementS6__Sb_T_ + 471
11  libswiftCore.dylib                  0x00000001086a8d9e _TPA__TFFSa4sortU__FRGSaQ__FFTQ_Q__SbT_U_FRGVSs26UnsafeMutableBufferPointerQ__T_ + 222
12  libswiftCore.dylib                  0x00000001086a8e18 _TPA__TTRG0_R_XFo_lGVSs26UnsafeMutableBufferPointerq___dT__XFo_lGS_q___iT__42 + 56
13  libswiftCore.dylib                  0x00000001085f7fda _TFSa30withUnsafeMutableBufferPointerU__fRGSaQ__U__FFRGVSs26UnsafeMutableBufferPointerQd___Q_Q_ + 522
14  libswiftCore.dylib                  0x00000001085f7db4 _TFSa4sortU__fRGSaQ__FFTQ_Q__SbT_ + 132
15  MSUapp                              0x0000000105761709 _TFC6MSUapp29FavoriteLeaguesViewController18generateLeagueListfS0_FT_T_ + 1097
16  MSUapp                              0x000000010576354b _TFC6MSUapp29FavoriteLeaguesViewController27numberOfSectionsInTableViewfS0_FCSo11UITableViewSi + 59
17  MSUapp                              0x00000001057635fa _TToFC6MSUapp29FavoriteLeaguesViewController27numberOfSectionsInTableViewfS0_FCSo11UITableViewSi + 58
18  UIKit                               0x000000010737cac3 -[UITableViewRowData _updateNumSections] + 84
19  UIKit                               0x000000010737d4b4 -[UITableViewRowData invalidateAllSections] + 69
20  UIKit                               0x00000001071c873b -[UITableView _updateRowData] + 217
21  UIKit                               0x00000001071de2b7 -[UITableView noteNumberOfRowsChanged] + 112
22  UIKit                               0x00000001071dd9f5 -[UITableView reloadData] + 1355
23  MSUapp                              0x00000001057647c6 _TFFC6MSUapp29FavoriteLeaguesViewController11viewDidLoadFS0_FT_T_U_FTO10RealmSwift12NotificationCS1_5Realm_T_ + 166
24  RealmSwift                          0x0000000105f37210 _TFF10RealmSwift41rlmNotificationBlockFromNotificationBlockFFT12notificationOS_12Notification5realmCS_5Realm_T_bTSSCSo8RLMRealm_T_U_FTSSS2__T_ + 224
25  RealmSwift                          0x0000000105f372af _TTRXFo_oSSoCSo8RLMRealm_dT__XFdCb_dCSo8NSStringdS__dT__ + 111
26  Realm                               0x0000000105c0645a -[RLMRealm sendNotifications:] + 986
27  Realm                               0x0000000105c068e6 -[RLMRealm commitWriteTransaction] + 262
28  Realm                               0x0000000105c06a48 -[RLMRealm transactionWithBlock:] + 120
29  RealmSwift                          0x0000000105f34250 _TFC10RealmSwift5Realm5writefS0_FFT_T_T_ + 176
30  MSUapp                              0x00000001056d46db _TZFC6MSUapp14DatabaseHelper23removeForSportAndSeasonfMS0_FTCS_5Sport6seasonSS_T_ + 603
31  MSUapp                              0x0000000105710d22 _TFFFC6MSUapp11AppDelegate14loadRemoteDataFS0_FT_T_U_FGSaCS_5Sport_T_U_FGSaCS_6League_T_ + 866
32  MSUapp                              0x0000000105710dc7 _TTRXFo_oGSaC6MSUapp6League__dT__XFo_iGSaS0___iT__ + 23
33  MSUapp                              0x00000001057103d1 _TPA__TTRXFo_oGSaC6MSUapp6League__dT__XFo_iGSaS0___iT__ + 81
34  MSUapp                              0x000000010575de90 _TTRXFo_iGSaC6MSUapp6League__iT__XFo_oGSaS0___dT__ + 32
35  MSUapp                              0x000000010575ddeb _TFZFC6MSUapp9APIHelper11loadLeaguesFMS0_FTSi18shouldWriteToRealmSb10completionGSqFGSaCS_6League_T___T_U_FCSo6NSDataT_ + 2763
36  MSUapp                              0x00000001056f4a0e _TTSf2n_n_n_n_n_d_i_n_n_n___TFFC6MSUapp14JSONDataSource18loadRemoteJsonDataFS0_FTSSCS_19GETParameterBuilderFCSo6NSDataT__T_U_FTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqS2__GSqCSo7NSError__T_ + 2302
37  MSUapp                              0x00000001056f2d59 _TPA__TTSf2n_n_n_n_n_d_i_n_n_n___TFFC6MSUapp14JSONDataSource18loadRemoteJsonDataFS0_FTSSCS_19GETParameterBuilderFCSo6NSDataT__T_U_FTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqS2__GSqCSo7NSError__T_ + 249
38  Alamofire                           0x00000001059e7599 _TTRXFo_oCSo12NSURLRequestoGSqCSo17NSHTTPURLResponse_oGSqCSo6NSData_oGSqCSo7NSError__dT__XFo_oS_oGSqS0__iGSqS1__oGSqS2___dT__ + 25
39  Alamofire                           0x00000001059e7461 _TFFFC9Alamofire7Request8responseFDS0_US_18ResponseSerializer___FT5queueGSqCSo8NSObject_18responseSerializerQ_17completionHandlerFTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqQ0__GSqCSo7NSError__T__DS0_U_FT_T_U_FT_T_ + 737
40  Alamofire                           0x00000001059e690e _TPA__TFFFC9Alamofire7Request8responseFDS0_US_18ResponseSerializer___FT5queueGSqCSo8NSObject_18responseSerializerQ_17completionHandlerFTCSo12NSURLRequestGSqCSo17NSHTTPURLResponse_GSqQ0__GSqCSo7NSError__T__DS0_U_FT_T_U_FT_T_ + 206
41  Alamofire                           0x00000001059a89d7 _TTRXFo__dT__XFdCb__dT__ + 39
42  libdispatch.dylib                   0x000000010938b186 _dispatch_call_block_and_release + 12
43  libdispatch.dylib                   0x00000001093aa614 _dispatch_client_callout + 8
44  libdispatch.dylib                   0x0000000109392a1c _dispatch_main_queue_callback_4CF + 1664
45  CoreFoundation                      0x00000001065741f9 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
46  CoreFoundation                      0x0000000106535dcb __CFRunLoopRun + 2043
47  CoreFoundation                      0x0000000106535366 CFRunLoopRunSpecific + 470
48  GraphicsServices                    0x000000010cc17a3e GSEventRunModal + 161
49  UIKit                               0x00000001070f08c0 UIApplicationMain + 1282
50  MSUapp                              0x000000010570f857 main + 135
51  libdyld.dylib                       0x00000001093df145 start + 1
52  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

这个调用堆栈让我可以假设,问题出在FavoriteLeaguesViewController的generateLeagueList方法中我的写入权限上。以下是该方法的主体:

var favorites = FavoritesHelper.sharedInstance.favoriteLeagues
favorites.sort { $0.sport < $1.sport }

for favorite in favorites {
  // Add to array, which we can later use for cellForRowAtIndexPath
}

favorites 的类型为 [League],其中 League 是一个 Realm 对象。我猜测异常出现的原因是我在访问 League 对象的属性,这些对象已经从 Realm 数据库中删除了(因为在 AppDelegate 中启动的 API 调用现在已经完成)。

那么我的问题是:我如何防止这种情况发生?我该如何确保在删除这些对象之前没有对任何 League 对象进行写入/读取访问?


尝试使用此链接,可能会对您有所帮助:https://dev59.com/SYnca4cB1Zd3GeqP9lr- - Anbu.Karthik
13个回答

65
您可以通过调用object.invalidated来检查Realm对象是否已被删除--如果它返回true,则它已被删除或Realm已手动失效。

28
在这种情况下,应该采取什么适当的行动?重新从 Realm 查询该对象吗?@segiddins - kbpontius
@kbpontius 要么这样做,要么清除对该对象的引用,具体取决于您的用例。 - Dannie P
4
我希望这个异常能告诉你被删除或作废的是哪个对象。这会减少很多无用的搜索。不过,堆栈跟踪也有一定帮助。 - Clifton Labrum
1
我也遇到了同样的错误。 但是我目前没有删除任何领域对象。相反,模型正在进行一些更新。 - Kudos

31

我找到了一种非常好的在Swift中捕获RLMException的方法。

当前的Swift不会显示RLMException发生的位置。

在Realm/RLMUtil.mm:266中,有关于RLMException的定义。

如果你改变代码来生成一个Swift错误,

Xcode现在可以展示异常发生的位置。

现在它是Swift的一部分了。

// Realm/RLMUtil.mm:266
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) {
    // add some code to generate a swift error. E.g. division-by-zero.
    int a = 0;
    if (reason == nil) {
        a = 1;
    }
    NSLog(@"calculating 1 / %d = %f", a, 1 / a);

    ... remainder of the original code...
}

1
非常好的调查工作! - Javier Calatrava Llavería
11
您能为Realm Swift项目做出贡献吗?这将有助于很多人。 - mylovemhz
4
@wjiee,能否请您解释一下您的解决方案?我在Swift中无法捕获RLMException。谢谢。 - Milan Kamilya
1
不再有 Realm/RLMUtil.mm:193 的痕迹了。这是在上面的回答之后的某个时候发生的。 - user1162328
3
在这个函数上添加断点比更改代码更好。 - Moosa Baloch
显示剩余3条评论

21

我刚刚在方法中设置了断点:

// Realm/RLMUtil.mm:193
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) {
    // ...
}

在左侧面板上,您可以检查堆栈跟踪,在这里您可以找到错误抛出的位置。


10
问题出在我的 FavoritesHelper 类中。它有两个属性:favoriteLeagueIDsfavoriteLeagues。我总是设置这两个属性,并将ID用于内部使用,而将另一个属性用于从这些联赛获取数据。
这意味着,所有喜爱的联赛都会被 favoriteLeagues 属性(类型为 [League])不断引用,因此当我想在它们失效后获取它们时,应用程序会崩溃。
为了解决这个问题,我已将属性 favoriteLeagues 更改为计算属性,如下所示:
var favoriteLeagues: [League] {
    get {
        var leagues = [League]()
        for id in favoriteLeagueIDs {
            if let league = realm.objectForPrimaryKey(League.self, key: id) {
                leagues.append(league)
            }
        }
        return leagues
    }
}

现在联赛不再被引用,只有在需要读取时才从数据库中加载。由于if let语句(Realm.objectForPrimaryKey(:key:)方法在这种情况下返回nil),无效或已删除的对象不会被加载。


1
那么如果对象在UITableView中显示,您的解决方案是在每个cellForRowAtIndexPath上查询数据库吗?您没有在ViewController中保留realm结果的引用吗? - Lucas P.
我也遇到了引用问题。我没有执行任何删除操作,但我的对象在某个地方被更新了,所以可能是因为这个原因导致了问题。 - Kudos

7

有人建议我在 Realm 框架内部设置断点,但我无法做到。相反,我在整个项目上放置了一个异常断点:

enter image description here

这使我能够在抛出异常时捕获正确的堆栈跟踪,并找到我的错误。


1
这实际上指引了我正确的方向 :) 谢谢! - sandpat

6

您可以调用isInvalidatedinvalidated来判断对象是否无效(YES)或有效(NO)。

您还可以编写自定义方法来定义什么是真正的invalidate

查找文档,我们将看到一个属性:

 /**
 Indicates if the object can no longer be accessed because it is now invalid.

 An object can no longer be accessed if the object has been deleted from the Realm that manages it, or
 if `invalidate` is called on that Realm.
 */
@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;

invalidated 是只读的,但是 isInvalidated 是什么意思呢?

它等同于 - (BOOL)invalidated { return invalidated; }

这意味着你可以编写一个自定义方法来定义你想要的真正的 invalidate


5
尝试在领域上创建元素而不是添加。
因此:
try! realm.write {
        realm.add(...)
    }

替换为:

try! realm.write {
        realm.create(...)
    }

然后,在删除操作之后,域应该按预期工作!


1
更改为创建已经解决了我的问题。在尝试了许多其他方法之后,谢谢您先生。 - StartPlayer

4
经过一天的努力,我发现在我的realm.delete()函数中,使用removeDispatchQueue.main.async后,最终解决了问题。"Original Answer"翻译成"最初的回答"。
DispatchQueue.main.async {
  realm.delete()
}

to

realm.delete()

1
完美的 SwiftUI Combine 解决方案,谢谢! - Santiago

3

根据我的经验,如果你在删除目标对象后仍然试图使用它,应用程序将会崩溃。

如果你想在删除 Realm 对象后触发一些代码块,只需在内存中删除对象之前尝试触发该块。在从内存中删除对象后尝试使用该对象会导致一些问题并使应用程序崩溃。

例如:

    try! realm.write {
        print("deleted word: \(targetObject.word)")
        realm.delete(targetObject)

        //  targetObject was removed, so don't try to access it otherwise you gonna got the 'nil' value instead of object.
    }

2

以下是我根据过去的经验总结的一些原因。

  1. 重写NSObject的isEqual(_ object: Any?)方法,然后返回被Realm刚刚删除的标识符。

我如何解决这个问题呢?

检查对象是否已失效,如果是,则返回false,否则继续使用你的标识符。例如:

extension YourModel {
    override func isEqual(_ object: Any?) -> Bool {
        guard let object = object as? YourModel,
              !self.isInvalidated else {
            return false
        }
        return object.id == self.id
    }
}

使用差异工具

对于那些使用 RxDataSources、IGListKit 等差异工具的人,您必须在尝试访问标识符之前执行与上述相同的检查。

如果您正在使用 RxDataSources 并且遇到由于采用 IdentifiableType 协议而导致的崩溃,则可以使用以下方法进行解决:

检查 Realm 对象是否已失效。 如果是,则只需返回一个随机唯一值,如下所示:

  var identity: String {
        return isInvalidated ? "deleted-object-\(UUID().uuidString)" : objectId
    }

来源:- https://github.com/RxSwiftCommunity/RxDataSources/issues/172#issuecomment-427675816

这个问题可能是由于您的数据源不符合RxDataSource的要求,因为它需要一个SectionModel类型的数组。如果您的数据源是一个普通的数组,则需要将其转换为SectionModel类型的数组。您可以使用RxDataSources中的toSectionModelArray()函数来完成此操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接