来自iOS10上的executeFetchRequest的奇怪结果

时间:2016-11-21 16:28:12

标签: ios core-data ios10

我刚遇到这个问题

+ (NSArray *)fetchMyStuffInContext:(NSManagedObjectContext *)_moc
{
  NSFetchRequest  *request;
  NSPredicate     *pred;
  __block NSError *error;
  __block NSArray *myStuff;

  request = [[NSFetchRequest alloc] initWithEntityName:@"myobs"];
  pred    = [NSPredicate predicateWithFormat:@"anAttribute == YES"];
  [request setPredicate:pred];

  error   = nil;
  [_moc performBlockAndWait:^{
    myStuff = [_moc executeFetchRequest:request error:&error];
  }];
  [request release];

  if (error) {
    NSLog(@"Error while fetching:\n%@",
      ([error localizedDescription] != nil) ?
        [error localizedDescription] : @"Unknown Error");
    return [NSArray array];
  }

  return myStuff;
}

返回iOS10上的ID数组和iOS9上的托管对象数组。

iOS9结果po myStuff

<_PFArray 0x16589ee0>(
  <MyObj: 0x16589bb0> (
    entity: myobs;
    id: 0xd000000000040000 <x-coredata://81E85B5C-6504-4269-974B-5AB4449658DC/myobs/p1> ;
    data: <fault>))

iOS10结果po myStuff

<__NSArrayM 0x170053e90>(
    0xd000000000040000 <x-coredata://81E85B5C-6504-4269-974B-5AB4449658DC/myobs/p1>
)

为什么?

在iOS 10上,我可以通过调用

来检索对象
[moc objectWithID:]

我在这里缺少什么?

1 个答案:

答案 0 :(得分:0)

好的我明白了!导致此问题的核心数据行为发生了变化。它甚至记录在Apple's What's new In CoreData

  

performBlockAndWait :隐式包含每个块周围的自动释放池。使用ARC或Swift的项目通常应该不受影响,但要记住NSError **输出参数和异常都是自动释放的,并且不能快乐地逃避阻止词法范围。使用手动保留/释放的项目需要保留自动释放的对象,包括executeFetchRequest的结果:error:在块内部并释放或自动释放它们在块的词法范围之外。

... Jaix

因此解决方案是在通过performBlockAndWait获取时保留并释放结果数组:

/* ... */
__block NSArray *result;

/* ... */

[moc performBlockAndWait:^{
  result = [[moc executeFetchRequest:request error:&error] retain];
}];

/* ... */

return [result autorelease];

奇怪的是,文档说明了

  

Core Data已针对使用[..] iOS 10.0 [..]的最小部署目标构建的应用程序更改了两种行为。

我的IPHONEOS_DEPLOYMENT_TARGET仍然设置为 6.0 ,所以我认为不是这样。

但是,我尝试使用从Xcode7链接的旧版 iPhoneOS9.2.sdk 进行构建。 只需将较旧的SDK链接到X​​code文件夹(并更新Info.plist中的MinimumSDKVersion),即使我在 10.1 上保留了BaseSDK项目设置,该错误也会消失。

我在视图控制器(带按钮;))中隔离了这个问题,如下所示:

#import <CoreData/CoreData.h>
#import "ViewController.h"

//------------------------------- Data entries. --------------------------------

@interface MyObj : NSManagedObject {}
  @property (nonatomic, retain) NSNumber *attribute;
@end
@implementation MyObj
  @dynamic attribute;
@end

//----------------------------- View controller. -------------------------------

@interface ViewController () {
  NSOperationQueue             *m_q;
  NSURL                        *m_dataURL;
  NSManagedObjectModel         *m_model;
  NSPersistentStoreCoordinator *m_store;
}
@end

@implementation ViewController

- (void) viewDidLoad
{
  [super viewDidLoad];
  m_q       = [[NSOperationQueue alloc] init];
  m_dataURL = [[[[[NSFileManager defaultManager]
                  URLsForDirectory:NSDocumentDirectory
                  inDomains:NSUserDomainMask]
                 lastObject]
                URLByAppendingPathComponent:@"mydata"]
               retain];
  m_model   = [[self _createModel] retain];
  m_store   = [[self _createStore:m_model] retain];
}

- (void) dealloc
{
  [m_q release];
  [m_dataURL release];
  [m_model release];
  [m_store release];

  [super dealloc];
}

- (void) refreshUI
{
  if (![NSThread isMainThread]) {
    [self performSelectorOnMainThread:@selector(refreshUI)
                           withObject:nil
                        waitUntilDone:NO];
    return;
  }
  [self _loadData];
}

- (IBAction) buttonClicked:(id)sender
{
  NSBlockOperation      *create;
  NSInvocationOperation *refresh;

  create = [[NSBlockOperation alloc] init];
  [create addExecutionBlock:^{
    [self _createAndSaveData];
  }];

  refresh = [[NSInvocationOperation alloc]
             initWithTarget:self
             selector:@selector(refreshUI)
             object:nil];
  [refresh addDependency:create];

  [m_q addOperation:create];
  [m_q addOperation:refresh];

  [create release];
  [refresh release];
}

//--------------------------- Simple model. ------------------------------------

- (NSManagedObjectModel *)_createModel
{
  NSManagedObjectModel   *model;
  NSEntityDescription    *entity;
  NSMutableArray         *properties;
  NSAttributeDescription *attribute;

  properties = [[NSMutableArray alloc] init];
  entity = [[NSEntityDescription alloc] init];
  [entity setName:@"myobj"];
  [entity setManagedObjectClassName:@"MyObj"];

  attribute = [[NSAttributeDescription alloc] init];
  [attribute setName:@"attribute"];
  [attribute setAttributeType:NSInteger32AttributeType];
  [properties addObject:attribute];
  [attribute release];

  [entity setProperties:properties];
  [properties release];

  model = [[NSManagedObjectModel alloc] init];
  [model setEntities:@[entity]];

  [entity release];

  return [model autorelease];
}

- (NSPersistentStoreCoordinator *)_createStore:(NSManagedObjectModel *)model
{
  NSPersistentStoreCoordinator *coordinator;
  NSError                      *error;
  NSPersistentStore            *store;

  error       = nil;
  coordinator = [[NSPersistentStoreCoordinator alloc]
                 initWithManagedObjectModel:model];
  store       = [coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                          configuration:nil
                                                    URL:m_dataURL
                                                options:nil
                                                  error:&error];
  if (!store) {
    [coordinator release];
    abort();
  }

  return [coordinator autorelease];
}

- (NSManagedObjectContext *)createContext:
    (NSManagedObjectContextConcurrencyType)type
{
  NSManagedObjectContext *ctx;

  ctx = [[NSManagedObjectContext alloc] initWithConcurrencyType:type];
  [ctx performBlockAndWait:^{
    [ctx setPersistentStoreCoordinator:m_store];
  }];

  return [ctx autorelease];
}

//----------------------------- Data actions. ----------------------------------

- (void) _createAndSaveData
{
  NSManagedObjectContext *moc;

  moc = [self createContext:NSPrivateQueueConcurrencyType];
  [moc performBlockAndWait:^{
    MyObj *obj;
    obj = [[MyObj alloc]
           initWithEntity:[[m_model entitiesByName] objectForKey:@"myobj"]
           insertIntoManagedObjectContext:moc];
    [obj setAttribute:
     [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]]];
    [moc save:nil];
    [obj release];
  }];
}

- (NSArray *)_loadData
{
  NSManagedObjectContext *moc;
  NSFetchRequest         *request;
  __block NSError        *error;
  __block NSArray        *objs;

  moc     = [self createContext:NSMainQueueConcurrencyType];
  request = [[NSFetchRequest alloc] initWithEntityName:@"myobj"];
  error   = nil;
  [moc performBlockAndWait:^{
    // Retain here because of new autorelease pool!
    objs = [[moc executeFetchRequest:request error:&error] retain];
  }];
  [request release];

  if (error) {
    return [NSArray array];
  }

  return [objs autorelease];
}

@end