945 lines
33 KiB
ReStructuredText
945 lines
33 KiB
ReStructuredText
|
==================================
|
||
|
Block Implementation Specification
|
||
|
==================================
|
||
|
|
||
|
.. contents::
|
||
|
:local:
|
||
|
|
||
|
History
|
||
|
=======
|
||
|
|
||
|
* 2008/7/14 - created.
|
||
|
* 2008/8/21 - revised, C++.
|
||
|
* 2008/9/24 - add ``NULL`` ``isa`` field to ``__block`` storage.
|
||
|
* 2008/10/1 - revise block layout to use a ``static`` descriptor structure.
|
||
|
* 2008/10/6 - revise block layout to use an unsigned long int flags.
|
||
|
* 2008/10/28 - specify use of ``_Block_object_assign`` and
|
||
|
``_Block_object_dispose`` for all "Object" types in helper functions.
|
||
|
* 2008/10/30 - revise new layout to have invoke function in same place.
|
||
|
* 2008/10/30 - add ``__weak`` support.
|
||
|
* 2010/3/16 - rev for stret return, signature field.
|
||
|
* 2010/4/6 - improved wording.
|
||
|
* 2013/1/6 - improved wording and converted to rst.
|
||
|
|
||
|
This document describes the Apple ABI implementation specification of Blocks.
|
||
|
|
||
|
The first shipping version of this ABI is found in Mac OS X 10.6, and shall be
|
||
|
referred to as 10.6.ABI. As of 2010/3/16, the following describes the ABI
|
||
|
contract with the runtime and the compiler, and, as necessary, will be referred
|
||
|
to as ABI.2010.3.16.
|
||
|
|
||
|
Since the Apple ABI references symbols from other elements of the system, any
|
||
|
attempt to use this ABI on systems prior to SnowLeopard is undefined.
|
||
|
|
||
|
High Level
|
||
|
==========
|
||
|
|
||
|
The ABI of ``Blocks`` consist of their layout and the runtime functions required
|
||
|
by the compiler. A ``Block`` of type ``R (^)(P...)`` consists of a structure of
|
||
|
the following form:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct Block_literal_1 {
|
||
|
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
|
||
|
int flags;
|
||
|
int reserved;
|
||
|
R (*invoke)(struct Block_literal_1 *, P...);
|
||
|
struct Block_descriptor_1 {
|
||
|
unsigned long int reserved; // NULL
|
||
|
unsigned long int size; // sizeof(struct Block_literal_1)
|
||
|
// optional helper functions
|
||
|
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
|
||
|
void (*dispose_helper)(void *src); // IFF (1<<25)
|
||
|
// required ABI.2010.3.16
|
||
|
const char *signature; // IFF (1<<30)
|
||
|
} *descriptor;
|
||
|
// imported variables
|
||
|
};
|
||
|
|
||
|
The following flags bits are in use thusly for a possible ABI.2010.3.16:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
enum {
|
||
|
// Set to true on blocks that have captures (and thus are not true
|
||
|
// global blocks) but are known not to escape for various other
|
||
|
// reasons. For backward compatibility with old runtimes, whenever
|
||
|
// BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
|
||
|
// non-escaping block returns the original block and releasing such a
|
||
|
// block is a no-op, which is exactly how global blocks are handled.
|
||
|
BLOCK_IS_NOESCAPE = (1 << 23),
|
||
|
|
||
|
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
|
||
|
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
|
||
|
BLOCK_IS_GLOBAL = (1 << 28),
|
||
|
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
|
||
|
BLOCK_HAS_SIGNATURE = (1 << 30),
|
||
|
};
|
||
|
|
||
|
In 10.6.ABI the (1<<29) was usually set and was always ignored by the runtime -
|
||
|
it had been a transitional marker that did not get deleted after the
|
||
|
transition. This bit is now paired with (1<<30), and represented as the pair
|
||
|
(3<<30), for the following combinations of valid bit settings, and their
|
||
|
meanings:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
switch (flags & (3<<29)) {
|
||
|
case (0<<29): 10.6.ABI, no signature field available
|
||
|
case (1<<29): 10.6.ABI, no signature field available
|
||
|
case (2<<29): ABI.2010.3.16, regular calling convention, presence of signature field
|
||
|
case (3<<29): ABI.2010.3.16, stret calling convention, presence of signature field,
|
||
|
}
|
||
|
|
||
|
The signature field is not always populated.
|
||
|
|
||
|
The following discussions are presented as 10.6.ABI otherwise.
|
||
|
|
||
|
``Block`` literals may occur within functions where the structure is created in
|
||
|
stack local memory. They may also appear as initialization expressions for
|
||
|
``Block`` variables of global or ``static`` local variables.
|
||
|
|
||
|
When a ``Block`` literal expression is evaluated the stack based structure is
|
||
|
initialized as follows:
|
||
|
|
||
|
1. A ``static`` descriptor structure is declared and initialized as follows:
|
||
|
|
||
|
a. The ``invoke`` function pointer is set to a function that takes the
|
||
|
``Block`` structure as its first argument and the rest of the arguments (if
|
||
|
any) to the ``Block`` and executes the ``Block`` compound statement.
|
||
|
|
||
|
b. The ``size`` field is set to the size of the following ``Block`` literal
|
||
|
structure.
|
||
|
|
||
|
c. The ``copy_helper`` and ``dispose_helper`` function pointers are set to
|
||
|
respective helper functions if they are required by the ``Block`` literal.
|
||
|
|
||
|
2. A stack (or global) ``Block`` literal data structure is created and
|
||
|
initialized as follows:
|
||
|
|
||
|
a. The ``isa`` field is set to the address of the external
|
||
|
``_NSConcreteStackBlock``, which is a block of uninitialized memory supplied
|
||
|
in ``libSystem``, or ``_NSConcreteGlobalBlock`` if this is a static or file
|
||
|
level ``Block`` literal.
|
||
|
|
||
|
b. The ``flags`` field is set to zero unless there are variables imported
|
||
|
into the ``Block`` that need helper functions for program level
|
||
|
``Block_copy()`` and ``Block_release()`` operations, in which case the
|
||
|
(1<<25) flags bit is set.
|
||
|
|
||
|
As an example, the ``Block`` literal expression:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
^ { printf("hello world\n"); }
|
||
|
|
||
|
would cause the following to be created on a 32-bit system:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct __block_literal_1 {
|
||
|
void *isa;
|
||
|
int flags;
|
||
|
int reserved;
|
||
|
void (*invoke)(struct __block_literal_1 *);
|
||
|
struct __block_descriptor_1 *descriptor;
|
||
|
};
|
||
|
|
||
|
void __block_invoke_1(struct __block_literal_1 *_block) {
|
||
|
printf("hello world\n");
|
||
|
}
|
||
|
|
||
|
static struct __block_descriptor_1 {
|
||
|
unsigned long int reserved;
|
||
|
unsigned long int Block_size;
|
||
|
} __block_descriptor_1 = { 0, sizeof(struct __block_literal_1) };
|
||
|
|
||
|
and where the ``Block`` literal itself appears:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct __block_literal_1 _block_literal = {
|
||
|
&_NSConcreteStackBlock,
|
||
|
(1<<29), <uninitialized>,
|
||
|
__block_invoke_1,
|
||
|
&__block_descriptor_1
|
||
|
};
|
||
|
|
||
|
A ``Block`` imports other ``Block`` references, ``const`` copies of other
|
||
|
variables, and variables marked ``__block``. In Objective-C, variables may
|
||
|
additionally be objects.
|
||
|
|
||
|
When a ``Block`` literal expression is used as the initial value of a global
|
||
|
or ``static`` local variable, it is initialized as follows:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct __block_literal_1 __block_literal_1 = {
|
||
|
&_NSConcreteGlobalBlock,
|
||
|
(1<<28)|(1<<29), <uninitialized>,
|
||
|
__block_invoke_1,
|
||
|
&__block_descriptor_1
|
||
|
};
|
||
|
|
||
|
that is, a different address is provided as the first value and a particular
|
||
|
(1<<28) bit is set in the ``flags`` field, and otherwise it is the same as for
|
||
|
stack based ``Block`` literals. This is an optimization that can be used for
|
||
|
any ``Block`` literal that imports no ``const`` or ``__block`` storage
|
||
|
variables.
|
||
|
|
||
|
Imported Variables
|
||
|
==================
|
||
|
|
||
|
Variables of ``auto`` storage class are imported as ``const`` copies. Variables
|
||
|
of ``__block`` storage class are imported as a pointer to an enclosing data
|
||
|
structure. Global variables are simply referenced and not considered as
|
||
|
imported.
|
||
|
|
||
|
Imported ``const`` copy variables
|
||
|
---------------------------------
|
||
|
|
||
|
Automatic storage variables not marked with ``__block`` are imported as
|
||
|
``const`` copies.
|
||
|
|
||
|
The simplest example is that of importing a variable of type ``int``:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
int x = 10;
|
||
|
void (^vv)(void) = ^{ printf("x is %d\n", x); }
|
||
|
x = 11;
|
||
|
vv();
|
||
|
|
||
|
which would be compiled to:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct __block_literal_2 {
|
||
|
void *isa;
|
||
|
int flags;
|
||
|
int reserved;
|
||
|
void (*invoke)(struct __block_literal_2 *);
|
||
|
struct __block_descriptor_2 *descriptor;
|
||
|
const int x;
|
||
|
};
|
||
|
|
||
|
void __block_invoke_2(struct __block_literal_2 *_block) {
|
||
|
printf("x is %d\n", _block->x);
|
||
|
}
|
||
|
|
||
|
static struct __block_descriptor_2 {
|
||
|
unsigned long int reserved;
|
||
|
unsigned long int Block_size;
|
||
|
} __block_descriptor_2 = { 0, sizeof(struct __block_literal_2) };
|
||
|
|
||
|
and:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct __block_literal_2 __block_literal_2 = {
|
||
|
&_NSConcreteStackBlock,
|
||
|
(1<<29), <uninitialized>,
|
||
|
__block_invoke_2,
|
||
|
&__block_descriptor_2,
|
||
|
x
|
||
|
};
|
||
|
|
||
|
In summary, scalars, structures, unions, and function pointers are generally
|
||
|
imported as ``const`` copies with no need for helper functions.
|
||
|
|
||
|
Imported ``const`` copy of ``Block`` reference
|
||
|
----------------------------------------------
|
||
|
|
||
|
The first case where copy and dispose helper functions are required is for the
|
||
|
case of when a ``Block`` itself is imported. In this case both a
|
||
|
``copy_helper`` function and a ``dispose_helper`` function are needed. The
|
||
|
``copy_helper`` function is passed both the existing stack based pointer and the
|
||
|
pointer to the new heap version and should call back into the runtime to
|
||
|
actually do the copy operation on the imported fields within the ``Block``. The
|
||
|
runtime functions are all described in :ref:`RuntimeHelperFunctions`.
|
||
|
|
||
|
A quick example:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
void (^existingBlock)(void) = ...;
|
||
|
void (^vv)(void) = ^{ existingBlock(); }
|
||
|
vv();
|
||
|
|
||
|
struct __block_literal_3 {
|
||
|
...; // existing block
|
||
|
};
|
||
|
|
||
|
struct __block_literal_4 {
|
||
|
void *isa;
|
||
|
int flags;
|
||
|
int reserved;
|
||
|
void (*invoke)(struct __block_literal_4 *);
|
||
|
struct __block_literal_3 *const existingBlock;
|
||
|
};
|
||
|
|
||
|
void __block_invoke_4(struct __block_literal_2 *_block) {
|
||
|
__block->existingBlock->invoke(__block->existingBlock);
|
||
|
}
|
||
|
|
||
|
void __block_copy_4(struct __block_literal_4 *dst, struct __block_literal_4 *src) {
|
||
|
//_Block_copy_assign(&dst->existingBlock, src->existingBlock, 0);
|
||
|
_Block_object_assign(&dst->existingBlock, src->existingBlock, BLOCK_FIELD_IS_BLOCK);
|
||
|
}
|
||
|
|
||
|
void __block_dispose_4(struct __block_literal_4 *src) {
|
||
|
// was _Block_destroy
|
||
|
_Block_object_dispose(src->existingBlock, BLOCK_FIELD_IS_BLOCK);
|
||
|
}
|
||
|
|
||
|
static struct __block_descriptor_4 {
|
||
|
unsigned long int reserved;
|
||
|
unsigned long int Block_size;
|
||
|
void (*copy_helper)(struct __block_literal_4 *dst, struct __block_literal_4 *src);
|
||
|
void (*dispose_helper)(struct __block_literal_4 *);
|
||
|
} __block_descriptor_4 = {
|
||
|
0,
|
||
|
sizeof(struct __block_literal_4),
|
||
|
__block_copy_4,
|
||
|
__block_dispose_4,
|
||
|
};
|
||
|
|
||
|
and where said ``Block`` is used:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct __block_literal_4 _block_literal = {
|
||
|
&_NSConcreteStackBlock,
|
||
|
(1<<25)|(1<<29), <uninitialized>
|
||
|
__block_invoke_4,
|
||
|
& __block_descriptor_4
|
||
|
existingBlock,
|
||
|
};
|
||
|
|
||
|
Importing ``__attribute__((NSObject))`` variables
|
||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
GCC introduces ``__attribute__((NSObject))`` on structure pointers to mean "this
|
||
|
is an object". This is useful because many low level data structures are
|
||
|
declared as opaque structure pointers, e.g. ``CFStringRef``, ``CFArrayRef``,
|
||
|
etc. When used from C, however, these are still really objects and are the
|
||
|
second case where that requires copy and dispose helper functions to be
|
||
|
generated. The copy helper functions generated by the compiler should use the
|
||
|
``_Block_object_assign`` runtime helper function and in the dispose helper the
|
||
|
``_Block_object_dispose`` runtime helper function should be called.
|
||
|
|
||
|
For example, ``Block`` foo in the following:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct Opaque *__attribute__((NSObject)) objectPointer = ...;
|
||
|
...
|
||
|
void (^foo)(void) = ^{ CFPrint(objectPointer); };
|
||
|
|
||
|
would have the following helper functions generated:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
void __block_copy_foo(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
|
||
|
_Block_object_assign(&dst->objectPointer, src-> objectPointer, BLOCK_FIELD_IS_OBJECT);
|
||
|
}
|
||
|
|
||
|
void __block_dispose_foo(struct __block_literal_5 *src) {
|
||
|
_Block_object_dispose(src->objectPointer, BLOCK_FIELD_IS_OBJECT);
|
||
|
}
|
||
|
|
||
|
Imported ``__block`` marked variables
|
||
|
-------------------------------------
|
||
|
|
||
|
Layout of ``__block`` marked variables
|
||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
The compiler must embed variables that are marked ``__block`` in a specialized
|
||
|
structure of the form:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_foo {
|
||
|
void *isa;
|
||
|
struct Block_byref *forwarding;
|
||
|
int flags; //refcount;
|
||
|
int size;
|
||
|
typeof(marked_variable) marked_variable;
|
||
|
};
|
||
|
|
||
|
Variables of certain types require helper functions for when ``Block_copy()``
|
||
|
and ``Block_release()`` are performed upon a referencing ``Block``. At the "C"
|
||
|
level only variables that are of type ``Block`` or ones that have
|
||
|
``__attribute__((NSObject))`` marked require helper functions. In Objective-C
|
||
|
objects require helper functions and in C++ stack based objects require helper
|
||
|
functions. Variables that require helper functions use the form:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_foo {
|
||
|
void *isa;
|
||
|
struct _block_byref_foo *forwarding;
|
||
|
int flags; //refcount;
|
||
|
int size;
|
||
|
// helper functions called via Block_copy() and Block_release()
|
||
|
void (*byref_keep)(void *dst, void *src);
|
||
|
void (*byref_dispose)(void *);
|
||
|
typeof(marked_variable) marked_variable;
|
||
|
};
|
||
|
|
||
|
The structure is initialized such that:
|
||
|
|
||
|
a. The ``forwarding`` pointer is set to the beginning of its enclosing
|
||
|
structure.
|
||
|
|
||
|
b. The ``size`` field is initialized to the total size of the enclosing
|
||
|
structure.
|
||
|
|
||
|
c. The ``flags`` field is set to either 0 if no helper functions are needed
|
||
|
or (1<<25) if they are.
|
||
|
|
||
|
d. The helper functions are initialized (if present).
|
||
|
|
||
|
e. The variable itself is set to its initial value.
|
||
|
|
||
|
f. The ``isa`` field is set to ``NULL``.
|
||
|
|
||
|
Access to ``__block`` variables from within its lexical scope
|
||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
In order to "move" the variable to the heap upon a ``copy_helper`` operation the
|
||
|
compiler must rewrite access to such a variable to be indirect through the
|
||
|
structures ``forwarding`` pointer. For example:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
int __block i = 10;
|
||
|
i = 11;
|
||
|
|
||
|
would be rewritten to be:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_i {
|
||
|
void *isa;
|
||
|
struct _block_byref_i *forwarding;
|
||
|
int flags; //refcount;
|
||
|
int size;
|
||
|
int captured_i;
|
||
|
} i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 };
|
||
|
|
||
|
i.forwarding->captured_i = 11;
|
||
|
|
||
|
In the case of a ``Block`` reference variable being marked ``__block`` the
|
||
|
helper code generated must use the ``_Block_object_assign`` and
|
||
|
``_Block_object_dispose`` routines supplied by the runtime to make the
|
||
|
copies. For example:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
__block void (voidBlock)(void) = blockA;
|
||
|
voidBlock = blockB;
|
||
|
|
||
|
would translate into:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_voidBlock {
|
||
|
void *isa;
|
||
|
struct _block_byref_voidBlock *forwarding;
|
||
|
int flags; //refcount;
|
||
|
int size;
|
||
|
void (*byref_keep)(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src);
|
||
|
void (*byref_dispose)(struct _block_byref_voidBlock *);
|
||
|
void (^captured_voidBlock)(void);
|
||
|
};
|
||
|
|
||
|
void _block_byref_keep_helper(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
|
||
|
//_Block_copy_assign(&dst->captured_voidBlock, src->captured_voidBlock, 0);
|
||
|
_Block_object_assign(&dst->captured_voidBlock, src->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
|
||
|
}
|
||
|
|
||
|
void _block_byref_dispose_helper(struct _block_byref_voidBlock *param) {
|
||
|
//_Block_destroy(param->captured_voidBlock, 0);
|
||
|
_Block_object_dispose(param->captured_voidBlock, BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER)}
|
||
|
|
||
|
and:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_voidBlock voidBlock = {( .forwarding=&voidBlock, .flags=(1<<25), .size=sizeof(struct _block_byref_voidBlock *),
|
||
|
.byref_keep=_block_byref_keep_helper, .byref_dispose=_block_byref_dispose_helper,
|
||
|
.captured_voidBlock=blockA )};
|
||
|
|
||
|
voidBlock.forwarding->captured_voidBlock = blockB;
|
||
|
|
||
|
Importing ``__block`` variables into ``Blocks``
|
||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
A ``Block`` that uses a ``__block`` variable in its compound statement body must
|
||
|
import the variable and emit ``copy_helper`` and ``dispose_helper`` helper
|
||
|
functions that, in turn, call back into the runtime to actually copy or release
|
||
|
the ``byref`` data block using the functions ``_Block_object_assign`` and
|
||
|
``_Block_object_dispose``.
|
||
|
|
||
|
For example:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
int __block i = 2;
|
||
|
functioncall(^{ i = 10; });
|
||
|
|
||
|
would translate to:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_i {
|
||
|
void *isa; // set to NULL
|
||
|
struct _block_byref_voidBlock *forwarding;
|
||
|
int flags; //refcount;
|
||
|
int size;
|
||
|
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
|
||
|
void (*byref_dispose)(struct _block_byref_i *);
|
||
|
int captured_i;
|
||
|
};
|
||
|
|
||
|
|
||
|
struct __block_literal_5 {
|
||
|
void *isa;
|
||
|
int flags;
|
||
|
int reserved;
|
||
|
void (*invoke)(struct __block_literal_5 *);
|
||
|
struct __block_descriptor_5 *descriptor;
|
||
|
struct _block_byref_i *i_holder;
|
||
|
};
|
||
|
|
||
|
void __block_invoke_5(struct __block_literal_5 *_block) {
|
||
|
_block->forwarding->captured_i = 10;
|
||
|
}
|
||
|
|
||
|
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
|
||
|
//_Block_byref_assign_copy(&dst->captured_i, src->captured_i);
|
||
|
_Block_object_assign(&dst->captured_i, src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
|
||
|
}
|
||
|
|
||
|
void __block_dispose_5(struct __block_literal_5 *src) {
|
||
|
//_Block_byref_release(src->captured_i);
|
||
|
_Block_object_dispose(src->captured_i, BLOCK_FIELD_IS_BYREF | BLOCK_BYREF_CALLER);
|
||
|
}
|
||
|
|
||
|
static struct __block_descriptor_5 {
|
||
|
unsigned long int reserved;
|
||
|
unsigned long int Block_size;
|
||
|
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
|
||
|
void (*dispose_helper)(struct __block_literal_5 *);
|
||
|
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5) __block_copy_5, __block_dispose_5 };
|
||
|
|
||
|
and:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_i i = {( .isa=NULL, .forwarding=&i, .flags=0, .size=sizeof(struct _block_byref_i), .captured_i=2 )};
|
||
|
struct __block_literal_5 _block_literal = {
|
||
|
&_NSConcreteStackBlock,
|
||
|
(1<<25)|(1<<29), <uninitialized>,
|
||
|
__block_invoke_5,
|
||
|
&__block_descriptor_5,
|
||
|
&i,
|
||
|
};
|
||
|
|
||
|
Importing ``__attribute__((NSObject))`` ``__block`` variables
|
||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
A ``__block`` variable that is also marked ``__attribute__((NSObject))`` should
|
||
|
have ``byref_keep`` and ``byref_dispose`` helper functions that use
|
||
|
``_Block_object_assign`` and ``_Block_object_dispose``.
|
||
|
|
||
|
``__block`` escapes
|
||
|
^^^^^^^^^^^^^^^^^^^
|
||
|
|
||
|
Because ``Blocks`` referencing ``__block`` variables may have ``Block_copy()``
|
||
|
performed upon them the underlying storage for the variables may move to the
|
||
|
heap. In Objective-C Garbage Collection Only compilation environments the heap
|
||
|
used is the garbage collected one and no further action is required. Otherwise
|
||
|
the compiler must issue a call to potentially release any heap storage for
|
||
|
``__block`` variables at all escapes or terminations of their scope. The call
|
||
|
should be:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
_Block_object_dispose(&_block_byref_foo, BLOCK_FIELD_IS_BYREF);
|
||
|
|
||
|
Nesting
|
||
|
^^^^^^^
|
||
|
|
||
|
``Blocks`` may contain ``Block`` literal expressions. Any variables used within
|
||
|
inner blocks are imported into all enclosing ``Block`` scopes even if the
|
||
|
variables are not used. This includes ``const`` imports as well as ``__block``
|
||
|
variables.
|
||
|
|
||
|
Objective C Extensions to ``Blocks``
|
||
|
====================================
|
||
|
|
||
|
Importing Objects
|
||
|
-----------------
|
||
|
|
||
|
Objects should be treated as ``__attribute__((NSObject))`` variables; all
|
||
|
``copy_helper``, ``dispose_helper``, ``byref_keep``, and ``byref_dispose``
|
||
|
helper functions should use ``_Block_object_assign`` and
|
||
|
``_Block_object_dispose``. There should be no code generated that uses
|
||
|
``*-retain`` or ``*-release`` methods.
|
||
|
|
||
|
``Blocks`` as Objects
|
||
|
---------------------
|
||
|
|
||
|
The compiler will treat ``Blocks`` as objects when synthesizing property setters
|
||
|
and getters, will characterize them as objects when generating garbage
|
||
|
collection strong and weak layout information in the same manner as objects, and
|
||
|
will issue strong and weak write-barrier assignments in the same manner as
|
||
|
objects.
|
||
|
|
||
|
``__weak __block`` Support
|
||
|
--------------------------
|
||
|
|
||
|
Objective-C (and Objective-C++) support the ``__weak`` attribute on ``__block``
|
||
|
variables. Under normal circumstances the compiler uses the Objective-C runtime
|
||
|
helper support functions ``objc_assign_weak`` and ``objc_read_weak``. Both
|
||
|
should continue to be used for all reads and writes of ``__weak __block``
|
||
|
variables:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
objc_read_weak(&block->byref_i->forwarding->i)
|
||
|
|
||
|
The ``__weak`` variable is stored in a ``_block_byref_foo`` structure and the
|
||
|
``Block`` has copy and dispose helpers for this structure that call:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
_Block_object_assign(&dest->_block_byref_i, src-> _block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
|
||
|
|
||
|
and:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
_Block_object_dispose(src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF);
|
||
|
|
||
|
In turn, the ``block_byref`` copy support helpers distinguish between whether
|
||
|
the ``__block`` variable is a ``Block`` or not and should either call:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_OBJECT | BLOCK_BYREF_CALLER);
|
||
|
|
||
|
for something declared as an object or:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
_Block_object_assign(&dest->_block_byref_i, src->_block_byref_i, BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BLOCK | BLOCK_BYREF_CALLER);
|
||
|
|
||
|
for something declared as a ``Block``.
|
||
|
|
||
|
A full example follows:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
__block __weak id obj = <initialization expression>;
|
||
|
functioncall(^{ [obj somemessage]; });
|
||
|
|
||
|
would translate to:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct _block_byref_obj {
|
||
|
void *isa; // uninitialized
|
||
|
struct _block_byref_obj *forwarding;
|
||
|
int flags; //refcount;
|
||
|
int size;
|
||
|
void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src);
|
||
|
void (*byref_dispose)(struct _block_byref_i *);
|
||
|
id captured_obj;
|
||
|
};
|
||
|
|
||
|
void _block_byref_obj_keep(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) {
|
||
|
//_Block_copy_assign(&dst->captured_obj, src->captured_obj, 0);
|
||
|
_Block_object_assign(&dst->captured_obj, src->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
|
||
|
}
|
||
|
|
||
|
void _block_byref_obj_dispose(struct _block_byref_voidBlock *param) {
|
||
|
//_Block_destroy(param->captured_obj, 0);
|
||
|
_Block_object_dispose(param->captured_obj, BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER);
|
||
|
};
|
||
|
|
||
|
for the block ``byref`` part and:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
struct __block_literal_5 {
|
||
|
void *isa;
|
||
|
int flags;
|
||
|
int reserved;
|
||
|
void (*invoke)(struct __block_literal_5 *);
|
||
|
struct __block_descriptor_5 *descriptor;
|
||
|
struct _block_byref_obj *byref_obj;
|
||
|
};
|
||
|
|
||
|
void __block_invoke_5(struct __block_literal_5 *_block) {
|
||
|
[objc_read_weak(&_block->byref_obj->forwarding->captured_obj) somemessage];
|
||
|
}
|
||
|
|
||
|
void __block_copy_5(struct __block_literal_5 *dst, struct __block_literal_5 *src) {
|
||
|
//_Block_byref_assign_copy(&dst->byref_obj, src->byref_obj);
|
||
|
_Block_object_assign(&dst->byref_obj, src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
|
||
|
}
|
||
|
|
||
|
void __block_dispose_5(struct __block_literal_5 *src) {
|
||
|
//_Block_byref_release(src->byref_obj);
|
||
|
_Block_object_dispose(src->byref_obj, BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK);
|
||
|
}
|
||
|
|
||
|
static struct __block_descriptor_5 {
|
||
|
unsigned long int reserved;
|
||
|
unsigned long int Block_size;
|
||
|
void (*copy_helper)(struct __block_literal_5 *dst, struct __block_literal_5 *src);
|
||
|
void (*dispose_helper)(struct __block_literal_5 *);
|
||
|
} __block_descriptor_5 = { 0, sizeof(struct __block_literal_5), __block_copy_5, __block_dispose_5 };
|
||
|
|
||
|
and within the compound statement:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
truct _block_byref_obj obj = {( .forwarding=&obj, .flags=(1<<25), .size=sizeof(struct _block_byref_obj),
|
||
|
.byref_keep=_block_byref_obj_keep, .byref_dispose=_block_byref_obj_dispose,
|
||
|
.captured_obj = <initialization expression> )};
|
||
|
|
||
|
truct __block_literal_5 _block_literal = {
|
||
|
&_NSConcreteStackBlock,
|
||
|
(1<<25)|(1<<29), <uninitialized>,
|
||
|
__block_invoke_5,
|
||
|
&__block_descriptor_5,
|
||
|
&obj, // a reference to the on-stack structure containing "captured_obj"
|
||
|
};
|
||
|
|
||
|
|
||
|
functioncall(_block_literal->invoke(&_block_literal));
|
||
|
|
||
|
C++ Support
|
||
|
===========
|
||
|
|
||
|
Within a block stack based C++ objects are copied into ``const`` copies using
|
||
|
the copy constructor. It is an error if a stack based C++ object is used within
|
||
|
a block if it does not have a copy constructor. In addition both copy and
|
||
|
destroy helper routines must be synthesized for the block to support the
|
||
|
``Block_copy()`` operation, and the flags work marked with the (1<<26) bit in
|
||
|
addition to the (1<<25) bit. The copy helper should call the constructor using
|
||
|
appropriate offsets of the variable within the supplied stack based block source
|
||
|
and heap based destination for all ``const`` constructed copies, and similarly
|
||
|
should call the destructor in the destroy routine.
|
||
|
|
||
|
As an example, suppose a C++ class ``FOO`` existed with a copy constructor.
|
||
|
Within a code block a stack version of a ``FOO`` object is declared and used
|
||
|
within a ``Block`` literal expression:
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
{
|
||
|
FOO foo;
|
||
|
void (^block)(void) = ^{ printf("%d\n", foo.value()); };
|
||
|
}
|
||
|
|
||
|
The compiler would synthesize:
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
struct __block_literal_10 {
|
||
|
void *isa;
|
||
|
int flags;
|
||
|
int reserved;
|
||
|
void (*invoke)(struct __block_literal_10 *);
|
||
|
struct __block_descriptor_10 *descriptor;
|
||
|
const FOO foo;
|
||
|
};
|
||
|
|
||
|
void __block_invoke_10(struct __block_literal_10 *_block) {
|
||
|
printf("%d\n", _block->foo.value());
|
||
|
}
|
||
|
|
||
|
void __block_literal_10(struct __block_literal_10 *dst, struct __block_literal_10 *src) {
|
||
|
FOO_ctor(&dst->foo, &src->foo);
|
||
|
}
|
||
|
|
||
|
void __block_dispose_10(struct __block_literal_10 *src) {
|
||
|
FOO_dtor(&src->foo);
|
||
|
}
|
||
|
|
||
|
static struct __block_descriptor_10 {
|
||
|
unsigned long int reserved;
|
||
|
unsigned long int Block_size;
|
||
|
void (*copy_helper)(struct __block_literal_10 *dst, struct __block_literal_10 *src);
|
||
|
void (*dispose_helper)(struct __block_literal_10 *);
|
||
|
} __block_descriptor_10 = { 0, sizeof(struct __block_literal_10), __block_copy_10, __block_dispose_10 };
|
||
|
|
||
|
and the code would be:
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
{
|
||
|
FOO foo;
|
||
|
comp_ctor(&foo); // default constructor
|
||
|
struct __block_literal_10 _block_literal = {
|
||
|
&_NSConcreteStackBlock,
|
||
|
(1<<25)|(1<<26)|(1<<29), <uninitialized>,
|
||
|
__block_invoke_10,
|
||
|
&__block_descriptor_10,
|
||
|
};
|
||
|
comp_ctor(&_block_literal->foo, &foo); // const copy into stack version
|
||
|
struct __block_literal_10 &block = &_block_literal; // assign literal to block variable
|
||
|
block->invoke(block); // invoke block
|
||
|
comp_dtor(&_block_literal->foo); // destroy stack version of const block copy
|
||
|
comp_dtor(&foo); // destroy original version
|
||
|
}
|
||
|
|
||
|
|
||
|
C++ objects stored in ``__block`` storage start out on the stack in a
|
||
|
``block_byref`` data structure as do other variables. Such objects (if not
|
||
|
``const`` objects) must support a regular copy constructor. The ``block_byref``
|
||
|
data structure will have copy and destroy helper routines synthesized by the
|
||
|
compiler. The copy helper will have code created to perform the copy
|
||
|
constructor based on the initial stack ``block_byref`` data structure, and will
|
||
|
also set the (1<<26) bit in addition to the (1<<25) bit. The destroy helper
|
||
|
will have code to do the destructor on the object stored within the supplied
|
||
|
``block_byref`` heap data structure. For example,
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
__block FOO blockStorageFoo;
|
||
|
|
||
|
requires the normal constructor for the embedded ``blockStorageFoo`` object:
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
FOO_ctor(& _block_byref_blockStorageFoo->blockStorageFoo);
|
||
|
|
||
|
and at scope termination the destructor:
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
FOO_dtor(& _block_byref_blockStorageFoo->blockStorageFoo);
|
||
|
|
||
|
Note that the forwarding indirection is *NOT* used.
|
||
|
|
||
|
The compiler would need to generate (if used from a block literal) the following
|
||
|
copy/dispose helpers:
|
||
|
|
||
|
.. code-block:: c++
|
||
|
|
||
|
void _block_byref_obj_keep(struct _block_byref_blockStorageFoo *dst, struct _block_byref_blockStorageFoo *src) {
|
||
|
FOO_ctor(&dst->blockStorageFoo, &src->blockStorageFoo);
|
||
|
}
|
||
|
|
||
|
void _block_byref_obj_dispose(struct _block_byref_blockStorageFoo *src) {
|
||
|
FOO_dtor(&src->blockStorageFoo);
|
||
|
}
|
||
|
|
||
|
for the appropriately named constructor and destructor for the class/struct
|
||
|
``FOO``.
|
||
|
|
||
|
To support member variable and function access the compiler will synthesize a
|
||
|
``const`` pointer to a block version of the ``this`` pointer.
|
||
|
|
||
|
.. _RuntimeHelperFunctions:
|
||
|
|
||
|
Runtime Helper Functions
|
||
|
========================
|
||
|
|
||
|
The runtime helper functions are described in
|
||
|
``/usr/local/include/Block_private.h``. To summarize their use, a ``Block``
|
||
|
requires copy/dispose helpers if it imports any block variables, ``__block``
|
||
|
storage variables, ``__attribute__((NSObject))`` variables, or C++ ``const``
|
||
|
copied objects with constructor/destructors. The (1<<26) bit is set and
|
||
|
functions are generated.
|
||
|
|
||
|
The block copy helper function should, for each of the variables of the type
|
||
|
mentioned above, call:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
_Block_object_assign(&dst->target, src->target, BLOCK_FIELD_<apropos>);
|
||
|
|
||
|
in the copy helper and:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
_Block_object_dispose(->target, BLOCK_FIELD_<apropos>);
|
||
|
|
||
|
in the dispose helper where ``<apropos>`` is:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
enum {
|
||
|
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
|
||
|
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
|
||
|
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
|
||
|
|
||
|
BLOCK_FIELD_IS_WEAK = 16, // declared __weak
|
||
|
|
||
|
BLOCK_BYREF_CALLER = 128, // called from byref copy/dispose helpers
|
||
|
};
|
||
|
|
||
|
and of course the constructors/destructors for ``const`` copied C++ objects.
|
||
|
|
||
|
The ``block_byref`` data structure similarly requires copy/dispose helpers for
|
||
|
block variables, ``__attribute__((NSObject))`` variables, or C++ ``const``
|
||
|
copied objects with constructor/destructors, and again the (1<<26) bit is set
|
||
|
and functions are generated in the same manner.
|
||
|
|
||
|
Under ObjC we allow ``__weak`` as an attribute on ``__block`` variables, and
|
||
|
this causes the addition of ``BLOCK_FIELD_IS_WEAK`` orred onto the
|
||
|
``BLOCK_FIELD_IS_BYREF`` flag when copying the ``block_byref`` structure in the
|
||
|
``Block`` copy helper, and onto the ``BLOCK_FIELD_<apropos>`` field within the
|
||
|
``block_byref`` copy/dispose helper calls.
|
||
|
|
||
|
The prototypes, and summary, of the helper functions are:
|
||
|
|
||
|
.. code-block:: c
|
||
|
|
||
|
/* Certain field types require runtime assistance when being copied to the
|
||
|
heap. The following function is used to copy fields of types: blocks,
|
||
|
pointers to byref structures, and objects (including
|
||
|
__attribute__((NSObject)) pointers. BLOCK_FIELD_IS_WEAK is orthogonal to
|
||
|
the other choices which are mutually exclusive. Only in a Block copy
|
||
|
helper will one see BLOCK_FIELD_IS_BYREF.
|
||
|
*/
|
||
|
void _Block_object_assign(void *destAddr, const void *object, const int flags);
|
||
|
|
||
|
/* Similarly a compiler generated dispose helper needs to call back for each
|
||
|
field of the byref data structure. (Currently the implementation only
|
||
|
packs one field into the byref structure but in principle there could be
|
||
|
more). The same flags used in the copy helper should be used for each
|
||
|
call generated to this function:
|
||
|
*/
|
||
|
void _Block_object_dispose(const void *object, const int flags);
|
||
|
|
||
|
Copyright
|
||
|
=========
|
||
|
|
||
|
Copyright 2008-2010 Apple, Inc.
|
||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
of this software and associated documentation files (the "Software"), to deal
|
||
|
in the Software without restriction, including without limitation the rights
|
||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
copies of the Software, and to permit persons to whom the Software is
|
||
|
furnished to do so, subject to the following conditions:
|
||
|
|
||
|
The above copyright notice and this permission notice shall be included in
|
||
|
all copies or substantial portions of the Software.
|
||
|
|
||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
THE SOFTWARE.
|