NSManagedObject改变内存地址?

时间:2015-06-07 15:01:52

标签: ios core-data nsmanagedobject

我为我的MO子类[FeedItem]设置了一个瞬态的,​​可转换的属性集,在一个类别中,我提供了延迟加载的访问:

- (id)images
{
    if (!self.sImages) {
        self.sImages = [[self.imageEntitiesClass alloc] initWithModel:self];
    }
    return self.sImages;
}

- (void)setImages:(id)images
{
    self.sImages = images;
}

现在,在-[FeedItem.managedObjectContext performBlock:]内,我致电-[FeedItem prefetchImages]。这样做,是执行以下调用堆栈:

-[FeedItem prefetchImages]
-[FeedItemImages avatar]
-[FeedItem avatarURL]
- MULTI-THREAD ASSERTION

-[FeedItemImages avatar]方法中,我调用self.model.avatarURL,但通过检查调试器,self.model.managedObjectContext与封装MOC不同,因此触发断言是有意义的..但是, MOC为何与众不同?我明确地在self中传递了-[FeedItemImages init],因此它们应该是同一个对象吗?

要确认此问题,我已禁用缓存,并且每次都返回一个新对象,该应用程序运行良好:

- (id)images
{
#warning TODO - underlying object is changing randomly?
    /** For some weird reason, when we cache image entities, then attempt to
     *  retrieve an image, we sometimes trigger a multithreading assertions 
     *  breakpoint. Debugger shows the owner of the image entity is different
     *  from the model the image entity is referencing ¯\_(ツ)_/¯
     *
     *  Possible solutions:
     *  The Bad:
     *  Current solution. Easy, but very ineffecient.
     *  The Ugly:
     *  Cache the image entities object privately, and expose a different 
     *  property that reassigns self every time.
     *  The Good:
     *  Firgure out when the object mutates (awake from fetch, or some other
     *  callback of CoreData) and invalidate the object there.
     */
    return [[self.imageEntitiesClass alloc] initWithModel:self];
}

当我们将根MOC作为主要内容时,这非常有效,并且可以动态创建子MOC以执行对象映射。

回溯:

    frame #0: [...] CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__] + 4
    frame #1: [...] CoreData`_sharedIMPL_pvfk_core + 221
  * frame #2: [...] Telly`Telly.TLYUserImages.feedAction.getter : Telly.TLYImageEntity(self=0x00007f84ca5cf6c0) + 416 at TLYUserImages.swift:26
    frame #3: [...] Telly`@objc Telly.TLYUserImages.feedAction.getter : Telly.TLYImageEntity + 34 at TLYUserImages.swift:0
    frame #4: [...] Foundation`-[NSObject(NSKeyValueCoding) valueForKey:] + 251
    frame #5: [...] Foundation`-[NSArray(NSKeyValueCoding) valueForKey:] + 437
    frame #6: [...] Foundation`-[NSObject(NSKeyValueCoding) valueForKeyPath:] + 245
    frame #7: [...] Foundation`-[NSArray(NSKeyValueCoding) valueForKeyPath:] + 435
    frame #8: [...] Foundation`-[NSObject(NSKeyValueCoding) valueForKeyPath:] + 261
    frame #9: [...] Foundation`-[NSArray(NSKeyValueCoding) valueForKeyPath:] + 435
    frame #10: [...] Foundation`-[NSObject(NSKeyValueCoding) valueForKeyPath:] + 261
    frame #11: [...] Foundation`-[NSArray(NSKeyValueCoding) valueForKeyPath:] + 435
    frame #12: [...] Telly`Telly.TLYMappingMeta.prefetch (target=AnyObject at 0x000000011e8ac858, self=0x00007f84ca423040)(forTarget : Swift.AnyObject) -> () + 361 at TLYMappingMeta.swift:75
    frame #13: [...] Telly`@objc Telly.TLYMappingMeta.prefetch (Telly.TLYMappingMeta)(forTarget : Swift.AnyObject) -> () + 54 at TLYMappingMeta.swift:0
    frame #14: [...] Telly`-[TLYMapTask _tag:with:using:in:](self=0x00007f84cecd64f0, _cmd=0x000000010aa12ee9, items=0x00007f84ca6d12e0, feedId=0x00007f84ce81ddf0, mapMeta=0x00007f84ca423040, moc=0x00007f84c9c89500) + 179 at TLYMapTask.m:42
    frame #15: [...] Telly`__39-[TLYMapTask _map:toMOC:sync:callback:]_block_invoke(.block_descriptor=<unavailable>) + 1920 at TLYMapTask.m:127
    frame #16: [...] CoreData`developerSubmittedBlockToNSManagedObjectContextPerform + 201
    frame #17: [...] libdispatch.dylib`_dispatch_client_callout + 8
    frame #18: 0x00000001107a76a7 libdispatch.dylib`_dispatch_queue_drain + 2176
    frame #19: 0x00000001107a6cc0 libdispatch.dylib`_dispatch_queue_invoke + 235
    frame #20: 0x00000001107aa3b9 libdispatch.dylib`_dispatch_root_queue_drain + 1359
    frame #21: 0x00000001107abb17 libdispatch.dylib`_dispatch_worker_thread3 + 111
    frame #22: 0x0000000110b2d637 libsystem_pthread.dylib`_pthread_wqthread + 729
    frame #23: 0x0000000110b2b40d libsystem_pthread.dylib`start_wqthread + 13

ImageEntities.swift

import Foundation

/** Each model object is composed of an imageEntities subclass that
 *  holds the image entities associated with that model.
 */
class TLYImageEntities: NSObject {

    unowned let model: AnyObject

    init(model: AnyObject) {
        self.model = model
    }
}

ImageEntities的示例子类。请注意self.user.avatarURL如何访问MO子类属性:

TLYUserImages:

import Foundation

class TLYUserImages: TLYImageEntities {

    var user: TVUser {
        return model as! TVUser
    }

    lazy var profileHeader: TLYImageEntity = TLYImageEntity(
        listItem: self.user,
        imageURL: self.user.avatarURL,
        formatName: TLYImageFormatUserProfileHeader,
        processor: TVImageProcessor.avatarProcessor()
    )

    ...

}

TVUser + Aggregator,提供图像实体类:

@implementation TVUser (Aggregator)

- (Class)imageEntitiesClass
{
    return [TLYUserImages class];
}

...

@end

2 个答案:

答案 0 :(得分:0)

因为在托管对象子类的实例上调用initWithModel:self,所以地址更改似乎是合乎逻辑的。 self指的是实例变量,所以无论何时用不同的变量调用它,你的神秘方法的参数内容也会有所不同。也许你只是插入一个新对象的聪明方法有点太聪明了。

也许你应该放弃这个几乎不可读且明显错误的方法并做一些更直观和直接的事情,就像一个类方法,它接受一个上下文并返回插入到该上下文中的新对象。

我同意上一张海报Marcus S. Zarra的历史悠久的名言:

  

咒语是一样的。保持代码简单。不要聪明,不要偷懒。做得对,它很容易维护。要聪明或懒惰,你只会惩罚自己和用户。

答案 1 :(得分:-1)

[[self.imageEntitiesClass alloc] initWithModel:self]做什么?具体是-initWithModel:吗?如此呼叫alloc init非常奇怪。

研究这种方法将有助于确定这个有趣的问题。