dispatch_data_create(3)

πŸ’– NAME πŸ’–

okay bestie, welcome to dispatch_data_create(3) from the Library Functions Manual β€” this is the whole girlboss squad of functions that create and manipulate dispatch data objects:

  • dispatch_data_create
  • dispatch_data_create_concat
  • dispatch_data_create_subrange
  • dispatch_data_create_map
  • dispatch_data_apply
  • dispatch_data_copy_region
  • dispatch_data_get_size

🌸 SYNOPSIS 🌸

#include <dispatch/dispatch.h>

dispatch_data_t
dispatch_data_create(const void* buffer, size_t size,
    dispatch_queue_t queue, dispatch_block_t destructor);

dispatch_data_t
dispatch_data_create_concat(dispatch_data_t data1,
    dispatch_data_t data2);

dispatch_data_t
dispatch_data_create_subrange(dispatch_data_t data, size_t offset,
    size_t length);

dispatch_data_t
dispatch_data_create_map(dispatch_data_t data, const void **buffer_ptr,
    size_t *size_ptr);

bool
dispatch_data_apply(dispatch_data_t data,
    bool (^applier)(dispatch_data_t, size_t, const void *, size_t));

dispatch_data_t
dispatch_data_copy_region(dispatch_data_t data, size_t location,
    size_t *offset_ptr);

size_t
dispatch_data_get_size(dispatch_data_t data);

dispatch_data_t dispatch_data_empty;

🫢 DESCRIPTION 🫢

So like, dispatch data objects are opaque containers of bytes that represent one or more regions of memory. They get created either from memory buffers managed by the application or the system, or from other dispatch data objects. ✨

They are immutable, and the memory regions they represent are required to remain unchanged for the lifetime of all data objects that reference them. No take-backs, no edits, the vibe is frozen. 🧊

The whole point? Dispatch data objects avoid copying the represented memory as much as possible. And multiple data objects can represent the same memory regions or subsections thereof β€” sharing is caring, no cap. πŸ’…


πŸŽ€ CREATION πŸŽ€

dispatch_data_create()

This one creates a new dispatch data object of given size from a buffer. The provided destructor block will be submitted to the specified queue when the object reaches the end of its lifecycle β€” which is the system saying "bestie, I no longer reference the buffer." That's your green light to deallocate the associated storage. πŸ™ˆ

The queue argument is ignored if one of the following predefined destructors is passed:
  • DISPATCH_DATA_DESTRUCTOR_FREE β€” indicates that the provided buffer can be deallocated with free(3) directly.
  • DISPATCH_DATA_DESTRUCTOR_DEFAULT β€” indicates that the provided buffer is not managed by the application and should be copied into memory managed and automatically deallocated by the system.

dispatch_data_create_concat()

Creates a new data object representing the concatenation of the memory regions represented by the provided data objects. Gluing them together, iconic behaviour. πŸ¦‹

dispatch_data_create_subrange()

Creates a new data object representing the sub-region of the provided data object specified by the offset and length parameters. Just a lil slice, a snack. πŸ“

dispatch_data_create_map()

Creates a new data object by mapping the memory represented by the provided data object as a single contiguous memory region (moving or copying memory as necessary).

If the buffer_ptr and size_ptr references are not NULL, they are filled with the location and extent of the contiguous region, which lets you do direct read access to the mapped memory. ✨

These values are valid only as long as the newly created object has not been released. Do not be caught slipping on this one.

🌈 ACCESS 🌈

dispatch_data_apply()

Gives you read access to represented memory without requiring it to be mapped as a single contiguous region. It traverses the memory regions represented by the data argument in logical order, invokes the specified applier block for each region, and returns a boolean indicating whether traversal completed successfully. πŸ‘‘

The applier block is passed the following arguments for each memory region, and returns a boolean indicating whether traversal should continue:

  • dispatch_data_t rgn β€” data object representing the region
  • size_t offset β€” logical position of the region in data
  • const void *loc β€” memory location of the region
  • size_t size β€” extent of the region
The rgn data object is released by the system when the applier block returns. And the associated memory location loc is valid only as long as rgn has not been deallocated. If you need loc outside of the applier block, you must retain the rgn object in the block. fr fr, don't skip this step. 😭

dispatch_data_copy_region()

Finds the contiguous memory region containing the logical position specified by the location argument among the regions represented by the provided data object, and returns a newly created copy of the data object representing that region. πŸ’«

The variable specified by the offset_ptr argument is filled with the logical position where the returned object starts in the data object.

dispatch_data_get_size()

Returns the logical size of the memory region or regions represented by the provided data object. That's it, that's the whole thing, she's simple and she slays. πŸ’…


🌟 EMPTY DATA OBJECT 🌟

The dispatch_data_empty object is the global singleton object representing a zero-length memory region. It is a valid input to any dispatch_data functions that take data object parameters. The smallest girl in the room and she still gets invited to everything. πŸ₯Ή


πŸ’œ MEMORY MODEL πŸ’œ

Dispatch data objects are retained and released via calls to dispatch_retain() and dispatch_release().

Data objects passed as arguments to a dispatch data create or copy function can be released when the function returns. The newly created object holds implicit references to their constituent memory regions as necessary. So like, the new object has your back. 🫢

The functions dispatch_data_create_map() and dispatch_data_apply() return an interior pointer to represented memory that is only valid as long as an associated object has not been released.

When Objective-C Automated Reference Counting is enabled, care needs to be taken if that object is held in a variable with automatic storage. It may need to be annotated with the objc_precise_lifetime attribute, or stored in a __strong instance variable instead, to ensure that the object is not released prematurely before memory accesses via the interior pointer have been completed.

⚠️ This is the part where people get got. Premature release = you're reading memory that's already moved out. Be careful, bestie.

πŸ‘€ SEE ALSO πŸ‘€


and that's the whole page, mwah πŸ’‹ β€” this was Darwin, dated December 1, 2010, and you just read the dispatch_data_create(3) manual. go forth and reference your memory regions responsibly, ily πŸŽ€βœ¨πŸ’–