libdispatch dispatch_data_apply 中的偏移量变量有什么意义?

What is the point of the offset variable in dispatch_data_apply for libdispatch?

我无法理解为 dispatch_io_read 函数调用提供给数据应用程序的偏移量变量。我看到 dispatch_data_apply 函数的 documentation claims the offset is the logical offset from the base of the data object. Looking at the source code 确认这个变量总是从 0 开始,第一次申请数据块,然后只是范围长度的总和。

我想我当时不明白这个变量的用途。我原本以为这是整个读取的偏移量,但事实并非如此。看来您必须跟踪读取的字节数和偏移量才能真正正确地在 libdispatch 中进行读取。

// Outside the dispatch_io_read handler...
char * currBufferPosition = destinationBuffer;

// Inside the dispatch_io_read handler...
dispatch_io_read(channel, fileOffset, bytesRequested, queue, ^(bool done, dispatch_data_t data, int error) {
  // Note: Real code would handle error variable.
  dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void * buffer, size_t size) {
    memcpy(currBufferPosition, buffer, size);
    currBufferPosition += size;
    return true;
  });
});

我的问题是:这是使用 dispatch_data_apply 返回的数据的正确方法吗?如果是这样,传递给应用程序处理程序的偏移量变量的目的是什么?文档对我来说似乎不清楚。

一个dispatch_data_t是一个字节序列。字节可以存储在多个 non-contiguous 字节数组中。例如,字节 0-6 可以存储在一个数组中,然后字节 7-12 存储在内存中其他地方的单独数组中。

为了提高效率,dispatch_data_apply 函数允许您迭代这些数组 in-place(无需复制数据)。在每次调用您的“applier”时,您都会收到一个指向 buffer 参数中底层存储数组之一的指针。 size 参数告诉您这个特定数组中有多少字节,offset 参数告诉您这个特定数组的第一个字节与整个 dispatch_data_t.

示例:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        dispatch_data_t aData = dispatch_data_create("Hello, ", 7, nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
        dispatch_data_t bData = dispatch_data_create("world!", 6, nil, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
        dispatch_data_t cData = dispatch_data_create_concat(aData, bData);

        dispatch_data_apply(cData, ^bool(dispatch_data_t  _Nonnull region, size_t offset, const void * _Nonnull buffer, size_t size) {
            printf("applying at offset %lu, buffer %p, size %lu, contents: [%*.*s]\n", (unsigned long)offset, buffer, (unsigned long)size, (int)size, (int)size, buffer);
            return true;
        });
    }
    return 0;
}

输出:

applying at offset 0, buffer 0x100407970, size 7, contents: [Hello, ]
applying at offset 7, buffer 0x1004087b0, size 6, contents: [world!]

好的,这就是 offset 参数的用途。现在这与 dispatch_io_read 有什么关系?

好吧,dispatch_io_read 不会将相同的字节传递给您两次。一旦它传递给你一些字节,它就会丢弃它们。下次它向您传递字节时,它们是新鲜的 newly-read 字节。如果您想要旧字节,则必须将它们放在自己身边。如果你想知道在当前调用你的回调之前你有多少旧字节,你必须自己计算。这不是 offset 参数的目的。

有可能当 dispatch_io_read 调用你时,它传递给你一个 dispatch_data_t,它已将其字节存储在多个 non-contiguous 数组中,所以当你调用 dispatch_data_apply它,你的应用程序被多次调用,具有不同的 offsets 和 buffers 和 sizes。但是这些调用只能让您访问当前调用回调的新字节,而不是之前调用回调的旧字节。