领域对象已被删除或失效
Posted
技术标签:
【中文标题】领域对象已被删除或失效【英文标题】:Realm object has been deleted or invalidated 【发布时间】:2015-10-29 10:13:16 【问题描述】:当我启动我的应用程序时,我会执行 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
是一个领域对象。我假设发生异常是因为我正在访问 League
对象的属性,同时这些对象已从 Realm 数据库中删除(因为在 AppDelegate 中启动的 API 调用现已完成)。
那么我的问题是:我怎样才能防止这种情况发生?如何确保在删除 League
对象之前不再有任何写入/读取访问权限?
【问题讨论】:
试试这个链接可能对你有帮助***.com/questions/29432191/… 【参考方案1】:您可以通过调用object.invalidated
来检查一个对象是否已从Realm 中删除——如果它返回true
,则表明该对象已被删除或Realm 已手动失效。
【讨论】:
此时应该采取什么正确的措施,从 Realm 重新查询对象? @segiddins @kbpontius 或者清除对该对象的引用,具体取决于您的用例。 我希望异常能告诉您哪个对象已被删除/无效。这将导致更少的野鹅追逐。不过,堆栈跟踪有点帮助。【参考方案2】:我有一个非常好的方法来在 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...
【讨论】:
非常好的调查工作! 你能把这个贡献给 Realm Swift 项目吗?这将帮助很多人 @wjiee weofej 将请解释您的解决方案,我无法迅速捕捉到 RLMException。谢谢 不再有 Realm/RLMUtil.mm:193 的踪迹。当然,这是在上述答案之后的某个时间。 在这个函数上加断点比改代码好。【参考方案3】:我刚刚在方法中放置了断点:
// Realm/RLMUtil.mm:193
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo)
// ...
您可以在左侧面板上查看堆栈跟踪,在这里您可以找到引发错误的位置。
【讨论】:
【参考方案4】:问题出在我的FavoritesHelper
班级中。它同时具有favoriteLeagueIDs
和favoriteLeagues
属性。我总是同时设置它们,并将 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
),无效或删除的对象不会被加载。
【讨论】:
所以如果对象显示在 UITableView 中,您的解决方案是查询每个 cellForRowAtIndexPath 上的 DB?您没有在 ViewController 中保留对领域结果的引用?【参考方案5】:我无法按照其他人的建议在 Realm 框架中放置断点,而是在我的整个项目中放置了异常断点:
这让我可以在抛出异常时捕获正确的堆栈跟踪,并找到我的错误。
【讨论】:
这实际上为我指明了正确的方向 :) 谢谢伙计!【参考方案6】:您可以调用isInvalidated
或invalidated
来判断对象无效(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
。
【讨论】:
【参考方案7】:尝试在领域中创建元素而不是添加
所以:
try! realm.write
realm.add(...)
替换为:
try! realm.write
realm.create(...)
然后在删除操作之后领域应该会按预期工作!
【讨论】:
更改为 create 解决了我的问题。在尝试了很多其他事情之后。谢谢你,先生。【参考方案8】:根据我的经验,如果您在删除后尝试使用目标对象(您想要删除的对象),应用程序将会崩溃。
如果您想在删除领域对象后触发一些代码块,只需尝试在内存中删除对象之前触发该块。在从内存中删除后尝试使用该对象,会产生一些问题并使应用程序崩溃。
例如:
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.
【讨论】:
【参考方案9】:在我的情况下,我一次从 2 个表中删除数据.. 一个与另一个不同。
let itemToDelete = counters[indexPath.row]
let realm = try! Realm()
try! realm.write
realm.delete(itemToDelete)
let predicate = NSPredicate(format: "counterid = \(c.id)")
let children = realm.objects(Details.self).filter(predicate)
realm.delete(children)
但问题是我试图删除不再存在的项目的子项。换个顺序,解决了!
let itemToDelete = counters[indexPath.row]
let realm = try! Realm()
try! realm.write
let predicate = NSPredicate(format: "counterid = \(c.id)")
let children = realm.objects(Details.self).filter(predicate)
realm.delete(children)
realm.delete(itemToDelete) //this should be deleted after
希望这对其他人有帮助!
【讨论】:
【参考方案10】:花了一天后,我发现我的realm.delete()
函数中的remove DispatchQueue.main.async
终于成功了。
DispatchQueue.main.async
realm.delete()
到
realm.delete()
【讨论】:
【参考方案11】:根据我过去的经验,这里有一些原因。
-
覆盖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
【讨论】:
【参考方案12】:您可以做的是观察从初始 Realm 查询(用于填充列表/表格视图的查询)返回的 Results<>
对象,并在数据库发生更改时在观察者回调方法中更新该列表。
只需确保使用与用于初始查询的对象相同的 Realm
实例来删除/修改对象。
编辑:
一些代码示例:
let realm = Realm()
let results = realm.objects(Favourite.self)
let notificationToken = results.observe [weak self] (changes) in
guard let tableView = self?.tableView else return
tableView.reloadData()
// Somewhere else
try? realm.write
if let favourite = results.first
realm.delete(favourite)
// OR
if let objectRealm = results.first?.realm
try? objectRealm.write
objectRealm.delete(results.first!)
// Don't forget to stop observing in deinit
// notificationToken.invalidate()
【讨论】:
以上是关于领域对象已被删除或失效的主要内容,如果未能解决你的问题,请参考以下文章