0

I always think the isa pointer points to the Class of the instantiation. And the Class's isa pointer points to the its metaclass. But in some article about the introduction of isa pointer, the author wrote: every bits in isa pointer can carry some message about the object. This like following:

[objc explain]: Non-pointer isa

从 NSObject 的初始化了解 isa

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 

But in my test, I found the low three bits were always 0, it just like:

enter image description here

And I had added the associated object in NSObject, but the second bit (has_assoc) is not 1 in isa pointer.

So how can I understand the isa pointer?

6
  • 1
    Did you test on the simulator or on a real device? Commented Sep 21, 2016 at 0:13
  • How old is the reference you found? The in-memory representation of an object can be changed any time Apple feels like it. Commented Sep 21, 2016 at 2:55
  • That post is from 2013 and contains the following: For entertainment purposes only. These values will change in future OS versions. I think they already have changed, actually. Commented Sep 21, 2016 at 2:57
  • 1
    @Avi If you know the reason, I hope you can give an answer. If you don't know, there is no need to waste time to comment me. : ) Commented Sep 21, 2016 at 9:08
  • You could check the Swift runtime source or the Foundation source, both available from Apple. Commented Sep 21, 2016 at 9:19

3 Answers 3

1

Maybe,there have two points that you should know.

First,in most cases,pass a nil value to a dictionary cause to remove the key form the dictionary.

Such as - (void)setObject:(ObjectType)object forKeyedSubscript:(id<NSCopying>)aKey and void objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy).

NSMutableDictionary *dic = [NSMutableDictionary dictionary]; dic[@"name"] = @"酷酷的哀殿"; dic[@"name"] = nil; NSLog(@"%@", dic);// it's output is `{}`. 

Second,you should know the byte-order of your target device.

You can test it by follow code.

int msg = 0x01020304; char *buffer = (char *)&msg; printf("%d %d %d %d\n", buffer[0], buffer[1], buffer[2], buffer[3]); 

In my x86-64 device,it's output is 4 3 2 1.

So,when I change has_assoc,the last but one byte will have a change.

And in pointer,it will add/minus 2.

isa: 0x1dffff795ca0f1 isa: 0x1dffff795ca0f3 

For isa_t,you can use follow code to get has_assoc.

#import <Foundation/Foundation.h> #import <objc/runtime.h> int main() { // extra_rc must be the MSB-most field (so it matches carry/overflow flags) // indexed must be the LSB (fixme or get rid of it) // shiftcls must occupy the same bits that a real class pointer would // bits + RC_ONE is equivalent to extra_rc + 1 // RC_HALF is the high bit of extra_rc (i.e. half of its range) // future expansion: // uintptr_t fast_rr : 1; // no r/r overrides // uintptr_t lock : 2; // lock for atomic property, @synch // uintptr_t extraBytes : 1; // allocated with extra bytes # if __arm64__ # define ISA_MASK 0x0000000ffffffff8ULL # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL struct isa_t { uintptr_t indexed : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 19; # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18) }; NSLog(@"arm64"); static void *someKey = &someKey; id object = [NSObject new]; struct isa_t *isa = (__bridge struct isa_t *)object; NSLog(@"%d", isa->has_assoc); objc_setAssociatedObject(object, someKey, @"Hello World!", OBJC_ASSOCIATION_RETAIN); NSLog(@"%d", isa->has_assoc); NSLog(@"%p", *(void * *)(__bridge void *)object); # elif __x86_64__ # define ISA_MASK 0x00007ffffffffff8ULL # define ISA_MAGIC_MASK 0x001f800000000001ULL # define ISA_MAGIC_VALUE 0x001d800000000001ULL struct isa_t { uintptr_t indexed : 1; uintptr_t has_assoc : 1; uintptr_t has_cxx_dtor : 1; uintptr_t shiftcls : 44; // MACH_VM_MAX_ADDRESS 0x7fffffe00000 uintptr_t magic : 6; uintptr_t weakly_referenced : 1; uintptr_t deallocating : 1; uintptr_t has_sidetable_rc : 1; uintptr_t extra_rc : 8; # define RC_ONE (1ULL<<56) # define RC_HALF (1ULL<<7) }; NSLog(@"x86_64"); static void *someKey = &someKey; id object = [NSObject new]; struct isa_t *isa = (__bridge struct isa_t *)object; NSLog(@"%d", isa->has_assoc); objc_setAssociatedObject(object, someKey, @"Hello World!", OBJC_ASSOCIATION_RETAIN); NSLog(@"%d", isa->has_assoc); NSLog(@"%p", *(void * *)(__bridge void *)object); # else // Available bits in isa field are architecture-specific. # error unknown architecture # endif return 0; } 
Sign up to request clarification or add additional context in comments.

Comments

0

Setting an associated object to a value of nil is a no-op, and will not be added to the associated objects side-table. Thus, it's isa tag does not change.

See the following example, to show that the tag bits do work:

NSObject *object = [NSObject new]; NSLog(@"isa: %p ", *(void **)(__bridge void *)object); static void *someKey = &someKey; objc_setAssociatedObject(object, someKey, nil, OBJC_ASSOCIATION_RETAIN); NSLog(@"isa: %p ", *(void **)(__bridge void *)object); 

For me, on OSX 10.11 outputs:

 2016-09-22 14:37:20.132 TestProj[95942:1117770] isa: 0x1dffff757a50f1 2016-09-22 14:37:24.208 TestProj[95942:1117770] isa: 0x1dffff757a50f1 

But if I change the value from nil to an actual pointer value:

objc_setAssociatedObject(object, someKey, @"Hello World!", OBJC_ASSOCIATION_RETAIN); 

I get the (expected) output, which includes the tagged bits being set in the pointer:

 2016-09-22 14:40:24.190 TestProj[96095:1121411] isa: 0x1dffff757a50f1 2016-09-22 14:40:24.191 TestProj[96095:1121411] isa: 0x1dffff757a50f3 

2 Comments

Thx a lot. But In my test, I can't get the result as your code. And if I change the value from nil to an actual pointer value, the output is same as ago. I test it in OSX 10.11, by xcode 8.0. And Apple LLVM version 8.0.0 (clang-800.0.38). Could you show me your proj about this example by screenshot?
Thank you very much. I get the true result by my iPhone. And I think it's the problem of the architecture.
-1

You tested on the simulator. The article you linked says this:

The 64-bit iOS simulator currently does not use non-pointer isa. Test your code on a real arm64 device.

You have to test on a device to see a non-pointer isa.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.