Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing things in isa

The 64-bit runtime took away the ability to directly access the isa field of an object, something CLANG engineers had been warning us about for a while. They've been replaced by a rather inventive (and magic) set of everchanging ABI rules about which sections of the newly christened isa header contain information about the object, or even other state (in the case of NSNumber/NSString). There seems to be a loophole, in that you can opt out of the new "magic" isa and use one of your own (a raw isa) at the expense of taking the slow road through certain runtime code paths.

My question is twofold, then:

If it's possible to opt out and object_setClass() an arbitrary class into an object in +allocWithZone:, is it also possible to put anything up there in the extra space with the class, or will the runtime try to read it through the fast paths?

What exactly in the isa header is tagged to let the runtime differentiate it from a normal isa?

like image 232
CodaFi Avatar asked Sep 25 '13 06:09

CodaFi


2 Answers

If it's possible to opt out and object_setClass() an arbitrary class into an object in +allocWithZone:

According to this article by Greg Parker

If you override +allocWithZone:, you may initialize your object's isa field to a "raw" isa pointer. If you do, no extra data will be stored in that isa field and you may suffer the slow path through code like retain/release. To enable these optimizations, instead set the isa field to zero (if it is not already) and then call object_setClass().

So yes, you can opt out and manually set a raw isa pointer. To inform the runtime about this, you have to the first LSB of the isa to 0. (see below)

Also, there's an environment variable that you can set, named OBJC_DISABLE_NONPOINTER_ISA, which is pretty self-explanatory.


is it also possible to put anything up there in the extra space with the class, or will the runtime try to read it through the fast paths?

The extra space is not being wasted. It's used by the runtime for useful in-place information about the object, such as the current state and - most importantly - its retain count (this is a big improvement since it used to be fetched every time from an external hash table).

So no, you cannot use the extra space for your own purposes, unless you opt out (as discussed above). In that case the runtime will go through the long path, ignoring the information contained in the extra bits.

Always according to Greg Parker's article, here's the new layout of the isa (note that this is very likely to change over time, so don't trust it)

(LSB)        
1 bit    |  indexed           | 0 is raw isa, 1 is non-pointer isa.
1 bit    |  has_assoc         | Object has or once had an associated reference. Object with no associated references can deallocate faster.
1 bit    |  has_cxx_dtor      | Object has a C++ or ARC destructor. Objects with no destructor can deallocate faster.
30 bits  |  shiftcls          | Class pointer's non-zero bits.
9 bits   |  magic             | Equals 0xd2. Used by the debugger to distinguish real objects from uninitialized junk.
1 bit    |  weakly_referenced | Object is or once was pointed to by an ARC weak variable. Objects not weakly referenced can deallocate faster.
1 bit    |  deallocating      | Object is currently deallocating.
1 bit    |  has_sidetable_rc  | Object's retain count is too large to store inline.
19 bits  |  extra_rc          | Object's retain count above 1. (For example, if extra_rc is 5 then the object's real retain count is 6.)
(MSB)    

What exactly in the isa header is tagged to let the runtime differentiate it from a normal isa?

As anticipated above you can discriminate between a raw isa and a new rich isa by looking at the first LSB.


To wrap it up, while it looks feasible to opt out and start messing with the extra bits available on a 64 bit architecture, I personally discourage it. The new isa layout is carefully crafted for optimizing the runtime performances and it's far from guaranteed to stay the same over time.

Apple may also decide in the future to drop the retro-compatibility with the raw isa representation, preventing opt out. Any code assuming the isa to be a pointer would then break.

like image 133
Gabriele Petronella Avatar answered Oct 04 '22 06:10

Gabriele Petronella


You can't safely do this, since if (when, really) the usable address space expands beyond 33 bits, the layout will presumably need to change again. Currently though, the bottom bit of the isa controls whether it's treated as having extra info or not.

like image 36
Catfish_Man Avatar answered Oct 04 '22 06:10

Catfish_Man