mulle_objc: object layout, retain counting, finalize
Continued from mulle-objc: hashes for classes, selectors and protocols
mulle_objc uses a uniform memory layout for all instances.
isa pointer is no longer part of the
self-> accessible instance variable area. Therefore
isa (currently) does not exist in mulle-objc.
You can access the isa pointer with
mulle_objc_object_get_classand it’s not unlikely, that I will teach the compiler to use that when accessing
self->isasome time in the future.
- Instances can be just casted to C struct pointers, if one desires
- C++ objects can be wrapped into ObjC objects, if one desires
- Tagged pointers for objects are impossible
- The first instance variable alignment is, depending on the mallocer, not made more worse than 64 bit alignment on 32 bit or 128 bit on 64 bit.
The retainCount is now always part of the memory layout. It gains a secondary meaning, when the retainCount is negative (though
-retainCount will never show negative values). Such an object is considered to be finalized.
finalize makes a comeback
Problems with the legacy retainCount/dealloc architecture
- due to the nature of NSAutoreleasePools it’s very hard to control
when exactly and in which order an objects’ resources are released. With
-performFinalize(see below) it can be done explicitly.
- the object that is executing
-deallocmay call a method on itself or call another object with itself as a parameter, which can lead to itself being retained. This can lead to misery and should be avoided. Move complex code to
The rules are pretty simple, when the retainCount is decremented to zero, an object gets the
-finalize message first (if
-finalize has not been called before) before
-dealloc. If the retainCount remains unchanged throughout
-dealloc is called. It is guaranteed that
-finalize is only called once.
If one wants to finalize “manually”, one should use
-performFinalize as this properly massages the retainCount. Directly calling
-finalize from outside the runtime is an error.
In most ObjC runtimes
-dealloc is used to free resources and cut
links to other objects. Now
-dealloc will ideally at most contain
-release calls and
[super dealloc]. Anything else can be done in
-finalize. The idea is, that a finalized object is still
useable in the object hierarchy, but not active anymore. A good example, where this is useful, is a window controller, where the window close button has been clicked. I, t may still redraw, but it doesn’t react to any event actions any more.
Points of interest
-finalizeis single-threaded by design, just like
-deallochave always been (which may be useful to know). But when you
-performFinalizeit can only be guaranteed that no other thread is executing
-finalize, not necessarily that no other thread is accessing the object. (More on that later)
That’s just the way it is, some things change
As I removed the flexibility of the way objects are structured and embedded the retainCount by decree, there is no real point in overriding
-retainCount anymore (except possibly for debugging). Because of this
-release are now compiled into nicely inlinable atomic functions, with no method or function call overhead.
Continued to mulle-objc: inheriting from protocols