How to Work with Properties and Ivar Layout in mulle‑objc‑runtime
In mulle‑objc‑runtime, properties compile into three linked structures—an ivar descriptor storing the byte offset, a property descriptor linking to that ivar and storing accessor method IDs, and the accessor implementations themselves—enabling zero‑overhead direct memory access while maintaining full introspection capabilities.
Working with properties and ivar layout in the mulle‑objc‑runtime requires understanding how the system decomposes Objective‑C properties into runtime metadata. Unlike traditional runtimes that hide these details, mulle‑objc exposes explicit structures in src/mulle-objc-ivar.h and src/mulle-objc-property.h that map every property to its backing storage and accessor methods. This architecture allows developers to enumerate instance variables, map properties to their backing ivars, and perform direct memory access using calculated offsets.
Understanding the Three‑Part Property System
In mulle‑objc, a property is not a single entity but a compile‑time description expanded into three tightly‑coupled pieces: the ivar descriptor, the property descriptor, and the accessor methods.
Ivar Descriptors and Layout
The ivar descriptor is defined in src/mulle-objc-ivar.h (lines 49‑66) as struct _mulle_objc_ivar. It contains the instance variable’s name, type‑encoding, unique ivar‑id, and crucially, the byte offset inside an object’s memory block. The offset is calculated when the class is registered and remains constant for the lifetime of the program, enabling stable direct memory access.
Property Descriptors and Metadata
The property descriptor lives in src/mulle-objc-property.h (lines 16‑27) as struct _mulle_objc_property. It stores the property name, type‑encoding (including attributes like readonly or copy), a unique property‑id, and the ivarid field that links the property to its backing storage. It also caches the getter and setter method‑ids in the getter and setter fields for fast dispatch.
Accessor Method Linkage
When a property is synthesized (@synthesize) or declared @dynamic, the compiler generates or expects accessor implementations. The property descriptor stores the method IDs for these accessors (getter, setter, adder, remover), allowing the runtime to connect property metadata to executable code without repeated string lookups.
Walking Ivars and Properties at Runtime
The runtime provides walker functions to enumerate ivars and properties, supporting both direct class inspection and inheritance traversal.
Enumerating Instance Variables
To walk all ivars including those inherited from superclasses, use _mulle_objc_infraclass_walk_ivars with the MULLE_OBJC_INHERIT_SUPERCLASS flag. This function is declared in src/mulle-objc-ivar.h and invokes a callback for each struct _mulle_objc_ivar.
#include <mulle-objc-runtime/mulle-objc-runtime.h>
#include <stdio.h>
static mulle_objc_walkcommand_t
print_ivar(struct _mulle_objc_infraclass *infra,
struct _mulle_objc_ivar *ivar,
void *info)
{
printf("Ivar: %-12s type: %-20s offset: %d\n",
mulle_objc_ivar_get_name(ivar),
mulle_objc_ivar_get_signature(ivar),
mulle_objc_ivar_get_offset(ivar));
return MULLE_OBJC_WALK_CONTINUE;
}
int main(void)
{
struct _mulle_objc_universe *universe;
struct _mulle_objc_infraclass *cls;
universe = mulle_objc_global_get_defaultuniverse();
cls = mulle_objc_universe_lookup_infraclass_nofail(
universe,
mulle_objc_classid_from_string("Person"));
if (!cls) return 1;
_mulle_objc_infraclass_walk_ivars(cls,
MULLE_OBJC_INHERIT_SUPERCLASS,
print_ivar, NULL);
return 0;
}
Key helper functions include mulle_objc_ivar_get_name(), mulle_objc_ivar_get_signature(), and mulle_objc_ivar_get_offset(), all defined in src/mulle-objc-ivar.h.
Looking Up a Single Ivar
For direct lookup without walking, use mulle_objc_infraclass_search_ivar, which searches the class’s ivar list by identifier.
struct _mulle_objc_ivar *ivar;
ivar = mulle_objc_infraclass_search_ivar(cls,
mulle_objc_ivarid_from_string("age"));
if (ivar)
{
int offset = mulle_objc_ivar_get_offset(ivar);
char *name = mulle_objc_ivar_get_name(ivar);
char *type = mulle_objc_ivar_get_signature(ivar);
printf("Found ivar %s (%s) at offset %d\n", name, type, offset);
}
Enumerating Properties
Properties are enumerated similarly using _mulle_objc_infraclass_walk_properties, implemented in src/mulle-objc-propertylist.c (lines 46‑70). The walker invokes a callback receiving struct _mulle_objc_property and the host class.
static mulle_objc_walkcommand_t
print_property(struct _mulle_objc_property *property,
struct _mulle_objc_infraclass *infra,
void *userinfo)
{
printf("Property: %-12s type: %-20s bits: 0x%04x\n",
mulle_objc_property_get_name(property),
mulle_objc_property_get_signature(property),
mulle_objc_property_get_bits(property));
return MULLE_OBJC_WALK_CONTINUE;
}
/* Usage */
_mulle_objc_infraclass_walk_properties(cls,
0,
print_property,
NULL);
Mapping Properties to Ivars
To understand how properties and ivar layout interact, map a property descriptor back to its backing ivar. The property’s ivarid field (defined in src/mulle-objc-property.h, lines 18‑20) stores the unique identifier of the associated instance variable.
struct _mulle_objc_property *prop;
struct _mulle_objc_ivar *ivar;
prop = _mulle_objc_infraclass_search_property(
cls, mulle_objc_propertyid_from_string("name"));
if (!prop) return;
ivar = mulle_objc_class_search_ivar(&cls->base, prop->ivarid);
printf("Property signature : %s\n",
mulle_objc_property_get_signature(prop));
printf("Backing ivar signature : %s\n",
ivar ? mulle_objc_ivar_get_signature(ivar) : "none");
This linkage confirms that when you work with properties and ivar layout in mulle‑objc, the property metadata directly references the ivar’s byte offset, allowing the runtime to synthesize accessors that read from or write to the correct memory location.
Direct Ivar Access Using Layout Offsets
For high‑performance scenarios, bypass accessor methods and access ivars directly using the offset stored in the ivar descriptor. This technique respects the ivar layout while eliminating message‑send overhead.
/* Assume 'person' is an instance and 'ivar' is the descriptor for 'age' */
int offset = mulle_objc_ivar_get_offset(ivar); /* e.g., 8 */
int *age_ptr = (int *)((char *)person + offset);
*age_ptr = 42; /* Direct memory write */
The offset is calculated once during class registration and remains immutable, providing C‑level performance while maintaining type safety through the ivar’s signature encoding. This pattern is particularly useful when you need to work with properties and ivar layout in performance‑critical loops where even cached method lookups are too expensive.
Key Source Files
When working with properties and ivar layout, reference these definitive source locations in the mulle‑objc‑runtime repository:
src/mulle-objc-ivar.h– Definesstruct _mulle_objc_ivarand accessor helpers likemulle_objc_ivar_get_offset().src/mulle-objc-property.h– Definesstruct _mulle_objc_propertyincluding theivaridlinkage field and attribute bits.src/mulle-objc-propertylist.c– Implements_mulle_objc_infraclass_walk_properties()and search helpers (lines 46‑70).src/mulle-objc-class.h– Providesmulle_objc_class_search_ivar()for cross‑referencing properties to ivars.book/chapter3-ivar-system.md– Narrative documentation of ivar layout and direct‑access patterns.book/chapter5-property-system.md– Overview of property metadata, synthesis, and introspection.
These files provide the complete implementation of how properties are tied to ivars, how the runtime stores layout offsets, and how you can query or manipulate them at runtime.
Summary
- Three‑part architecture: Properties decompose into ivar descriptors (layout offsets), property descriptors (metadata and linkage), and accessor method IDs.
- Ivar layout stability: Offsets are computed at class registration and stored in
struct _mulle_objc_ivar, enabling direct memory access viamulle_objc_ivar_get_offset(). - Property‑to‑ivar mapping: The
ivaridfield instruct _mulle_objc_propertylinks properties to their backing storage, resolvable viamulle_objc_class_search_ivar(). - Runtime inspection: Use
_mulle_objc_infraclass_walk_ivars()withMULLE_OBJC_INHERIT_SUPERCLASSto traverse inherited ivars, and_mulle_objc_infraclass_walk_properties()for property enumeration. - Zero‑overhead access: Direct ivar manipulation using calculated offsets bypasses message dispatch while respecting the runtime’s type encodings.
Frequently Asked Questions
How does mulle‑objc‑runtime store the byte offset for instance variables?
The runtime stores the byte offset inside struct _mulle_objc_ivar, defined in src/mulle-objc-ivar.h. The offset is calculated when the class is registered and remains constant for the lifetime of the program. You retrieve it using mulle_objc_ivar_get_offset(), which returns the byte distance from the object’s start address to the ivar’s location.
What is the relationship between a property and its backing ivar in mulle‑objc?
A property links to its backing ivar through the ivarid field in struct _mulle_objc_property (defined in src/mulle-objc-property.h). When a property is synthesized, the compiler creates an ivar entry and populates the property descriptor’s ivarid with that ivar’s unique identifier. At runtime, you can resolve this connection by calling mulle_objc_class_search_ivar() with the property’s ivarid.
Can I access instance variables directly without using property accessors?
Yes. You can bypass accessor methods by using the offset stored in the ivar descriptor. Call mulle_objc_ivar_get_offset() to get the byte offset, then calculate the memory address by adding that offset to the object pointer. This technique provides C‑level performance while maintaining type safety through the ivar’s signature encoding.
Where are the property and ivar walk functions implemented?
The ivar walk function _mulle_objc_infraclass_walk_ivars() is declared in src/mulle-objc-ivar.h and supports inheritance flags like MULLE_OBJC_INHERIT_SUPERCLASS. The property walk function _mulle_objc_infraclass_walk_properties() is implemented in src/mulle-objc-propertylist.c (lines 46‑70), with search helpers like _mulle_objc_infraclass_search_property() available for direct lookups by property ID.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →