【问题标题】:NSFetchRequest's fetchBatchSize causes all batches to be fetched immediately in SwiftNSFetchRequest 的 fetchBatchSize 导致所有批次在 Swift 中立即获取
【发布时间】:2017-08-01 13:21:00
【问题描述】:

我无法使 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 个批次的 fetch 请求,因为在返回的数组中访问了项目。使用 Core Data 工具或使用 -com.apple.CoreData.SQLDebug 标志进行分析会显示此行为。

在 Swift 中的行为似乎被破坏了。 执行 fetch 请求并将其存储在 fetchResults 变量中后,会自动以 20 个为一组自动发出一系列 fetch 请求,而无需任何额外的代码访问返回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 中执行 fetch 请求,返回的数组就可以交给 Swift 代码,并且在访问数组元素时批处理可以正常工作。仅当在 Swift 中执行 fetch 请求时,行为不正确。

目标-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 数组,并在访问数组中的元素时构造和执行获取请求,在从存储中获取对象时填充支持数组。这提供了一种实现类似于使用NSFetchRequestfetchBatchSize 的获取行为的方法,但它不需要用Objective-C 编写任何内容。它还避免了在桥接到 Swift 时遇到这个错误的微妙之处。

【问题讨论】:

    标签: objective-c swift macos core-data


    【解决方案1】:

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

    如果您将fetch 方法的返回类型保留为NSArray&lt;Item&gt;,则不需要迭代元素。

    【讨论】:

    • 感谢您的回复。 Swift 将 NSManagedObjectContext 的 fetch 方法导入为 public func fetch&lt;T : NSFetchRequestResult&gt;(_ request: NSFetchRequest&lt;T&gt;) throws -&gt; [T] 返回 Swift 数组(值类型)。一旦执行该方法,对象就会被迭代,并且批处理会针对整个数组进行。我不确定如何仅使用 Swift 来解决这个问题,NSArray 不能专门用于 Swift,因为 Swift 不会将它作为通用导入。不幸的是,这排除了使用 NSArray 作为返回类型。
    猜你喜欢
    • 2018-11-16
    • 2018-12-16
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-04
    相关资源
    最近更新 更多