This is the same behavior than 'classic' external ivars, but the memory
is allocated along with the instance itself (remember: the question is
'no extra malloc call'). The pointer to external ivars is not stored as
is, but is computed with self and instance_size. Still an 'extra load',
but not more than with a compiler-side solution. I even think this is a
'hand made' non-fragile ivar system, with no need for compiler support.
And the alignment issue seems to be solved with padding in
NSObject.m:334-371
All true but you need consider the assembler code that the compiler
emits to access the ivar in each class. And here the parent and the
subclass will disagree on what the offset to a_ext is.
This offset is currently an offset fixed at compile time by the
compiler's calculation of:
struct {
@defs(ParentClass)
} *obj;
&obj[1] /* the address of a_ext */
which cannot take into account the ivars of potential subclasses, which
will be at that exact same address (i.e &obj[1] == &b).
It takes the "non-fragile ivars" to replace the fixed value (calculated
and emitted by the compiler) into a lookup to the actual offset/location
of the value. This mechanism can only be provided by a newer compiler
emitting that lookup code instead of using the fixed offsets.
Hope that makes it clear why using the extra data prevents subclasses
from adding ivars.