NSFetchRequest的fetchBatchSize导致在Swift中立即获取所有批次

时间:2017-03-11 04:05:45

标签: objective-c swift macos core-data

我无法使NSFetchRequest fetchBatchSize正常工作。我正在测试:

  • Swift 3.0,Xcode 8.2.1,macOS 10.12 SDK,OS X 10.11.6
  • Swift 3.1,Xcode 8.3 b4,macOS 10.12 SDK,macOS 10.12.3

Objective-C中的行为按预期工作。返回的fetchResults数组正确执行20个批处理中的获取请求,因为在返回的数组中访问了项目。使用Core Data工具或使用-com.apple.CoreData.SQLDebug标志进行性能分析会显示此行为。

Swift中的行为显示已损坏。执行获取请求并将其存储在fetchResults变量中后,将自动批量生成一系列获取请求,而无需任何其他代码访问返回的fetchResults数组。也许Swift正在制作数组的副本或以某种方式访问​​数组中的每个元素,从而触发整个数组的自动批处理?

com.apple.CoreData.SQLDebug 3选项将每个批量提取请求显示为:

2017-03-10 20:57:23.698 TestApp[25937:5366493] CoreData: annotation: Bound intarray _Z_intarray0
2017-03-10 20:57:23.699 TestApp[25937:5366493] CoreData: details: Bound intarray value 1 at 0
2017-03-10 20:57:23.700 TestApp[25937:5366493] CoreData: details: Bound intarray value 2 at 1
2017-03-10 20:57:23.700 TestApp[25937:5366493] CoreData: details: Bound intarray value 3 at 2
2017-03-10 20:57:23.700 TestApp[25937:5366493] CoreData: details: Bound intarray value 4 at 3
…
2017-03-10 20:57:23.728 TestApp[25937:5366493] CoreData: details: Bound intarray value 20 at 19
2017-03-10 20:57:23.728 TestApp[25937:5366493] CoreData: annotation: Bound intarray values.
2017-03-10 20:57:23.728 TestApp[25937:5366493] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME FROM ZITEM t0 WHERE  t0.Z_PK IN (SELECT * FROM _Z_intarray0)   LIMIT 20
2017-03-10 20:57:23.730 TestApp[25937:5366493] CoreData: annotation: sql connection fetch time: 0.0316s
2017-03-10 20:57:23.730 TestApp[25937:5366493] CoreData: annotation: fetch using NSSQLiteStatement <0x60800008dc00> on entity 'Item' with sql text 'SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZNAME FROM ZITEM t0 WHERE  t0.Z_PK IN (SELECT * FROM _Z_intarray0)   LIMIT 20' returned 20 rows with values: (
    "<Item: 0x6080000aa380> (entity: Item; id: 0x40000b <x-coredata://7B347406-2EDA-465D-B002-B392D3DE9CF4/Item/p1> ; data: <fault>)",
    …
    "<Item: 0x6080000aa740> (entity: Item; id: 0x500000b <x-coredata://7B347406-2EDA-465D-B002-B392D3DE9CF4/Item/p20> ; data: <fault>)"
)

这可能会发生什么?只要在Objective-C中执行获取请求,返回的数组就可以传递给Swift代码,并且在访问数组元素时批处理工作正常。只有在Swift中执行获取请求时,行为才是错误的。

目标-C:

- (NSArray<Item *> *)fetch {
    NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"Item"];
    request.fetchBatchSize = 20;
    NSArray *fetchResults = [self.context executeFetchRequest:request error:nil];
    return fetchResults;
}

夫特:

func fetch() -> [Item] {
    let request = NSFetchRequest<Item>(entityName: "Item")
    request.fetchBatchSize = 20
    let fetchResults = (try? self.context.fetch(request)) ?? []
    return fetchResults
}

执行获取并将结果存储在属性中。

var items: [Item]!

func fetchItems() {
    if let fetcher = SwiftFetcher(context: document.managedObjectContext) {
        items = fetcher.fetch()
    }
}

我当前的解决方法涉及编写自定义FetchedBatchArray类,该类包装Swift数组并在访问数组中的元素时构造和执行获取请求,在从存储中获取对象时填充后备数组。这提供了一种实现类似于使用NSFetchRequest fetchBatchSize的提取行为的方法,但它并不需要在Objective-C中编写任何内容。它还避免了在连接到Swift时遇到这个bug的微妙之处。

1 个答案:

答案 0 :(得分:0)

Swift Array类型是一种值类型,从NSArray到Swift数组的自动桥接基本上遍历整个数组并将元素复制到新的Swift数组中。如果从调试器中调用po array,则会发生同样的情况。

如果将fetch方法的返回类型保留为NSArray<Item>,则不需要迭代这些元素。