<< [objc explain]: Classes and metaclasses | archive | Infinity isn't as long as you think >>
Non-fragile instance variables are a headline feature of the modern Objective-C runtime available on iPhone and 64-bit Mac. They provide framework developers more flexibility without losing binary compatibility, and pave the way for automatically-synthesized property ivars and ivars declared outside a class's interface.
Fragile ivars are a subset of the classic fragile base class problem. In some languages, a superclass cannot be changed without also recompiling all subclasses of that class. For example, adding data members or virtual member functions to a C++ superclass will break binary compatibility with any subclass of that class, even if the added members are private and invisible to the subclass.
In classic Objective-C, methods are mostly non-fragile, thanks to dynamic message dispatch. You can freely add methods to a superclass, as long as you don't have name conflicts. But Objective-C ivars are fragile on 32-bit Mac.
Say you're writing the world's next great pet shop
application for Mac OS X Leopard. You might have this
PetShopView
subclass of
NSView
, with arrays for the puppies and kittens in
the pet shop.
NSView (Leopard)
0
Class isa
4
NSRect bounds
20
NSView *superview
24
NSColor *bgColor
PetShopView
0
Class isa
4
NSRect bounds
20
NSView *superview
24
NSColor *bgColor
28
NSArray *kittens
32
NSArray *puppies
Then Mac OS X Def Leopard comes out, with its new multi-paw
interface technology. The AppKit developers add some paw-tracking
code to NSView
.
NSView (Def Leopard)
0
Class isa
4
NSRect bounds
20
NSView *superview
24
NSColor *bgColor
28
NSSet *touchedPaws
PetShopView
0
Class isa
4
NSRect bounds
20
NSView *superview
24
NSColor *bgColor
28
(削除) NSArray *kittens (削除ここまで)
32
NSArray *puppies
Unfortunately, your kittens are doomed by fragile ivars. Alternatively, the AppKit developers are trapped with whatever ivars they chose in Mac OS X 10.0.
What you and the AppKit developers really want is something like this.
NSView (Def Leopard)
0
Class isa
4
NSRect bounds
20
NSView *superview
24
NSColor *bgColor
28
NSSet *touchedPaws
PetShopView
0
Class isa
4
NSRect bounds
20
NSView *superview
24
NSColor *bgColor
28
NSSet *touchedPaws
32
NSArray *kittens
36
NSArray *puppies
Here, the runtime has recognized that NSView
is now
larger than it was when PetShopView
was compiled. The
subclass ivars slide in response, without recompiling any code,
and the kittens are saved by a dynamic runtime.
The generated code for classic Objective-C ivar access works like
a C struct
field. The offset to the ivar is a
constant determined at compile time. The new ivar code instead
creates a variable for each ivar which contains the offset to that
ivar, and all code that accesses the ivar uses that
variable. At launch time, the runtime can change any ivar offset
variable if it detects an oversize superclass.
In the pet shop example,
_OBJC_IVAR_PetShopView_kittens
is 28 at
compile time,
but the runtime changes it to 32 when it sees the Def Leopard
version of NSView
. No code needs to be recompiled,
and the performance overhead of the extra ivar offset variable is
small. AppKit is happy, you're happy, and the kittens are happy.