`isa` 指针是否携带有关 Objective-C 中实例化的其他消息?

Does `isa` pointer carry other message about an instantiation in Objective-C?

我一直认为isa指针指向实例化的Class。 Class 的 isa 指针指向它的 metaclass。 但是在一些关于isa指针介绍的文章中,作者写道:isa指针中的每一位都可以携带一些关于对象的信息。如下所示:

[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    

但是在我的测试中,我发现低三位总是0,就像:

并且我在NSObject中添加了关联对象,但是isa指针中的第二位(has_assoc)不是1 .

那么如何理解isa指针呢?

将关联对象设置为 nil 的值是空操作,不会添加到关联对象端 -table。因此,它的 isa 标签不会改变。

请参阅以下示例,以显示标记位确实有效:

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);

对我来说,OSX 10.11 输出:

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

但是如果我将值从 nil 更改为实际指针值:

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

我得到了(预期的)输出,其中包括在指针中设置的标记位:

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

也许,有两点你应该知道。

首先,在大多数情况下,将 nil 值传递给字典,导致从字典中删除键。

比如- (void)setObject:(ObjectType)object forKeyedSubscript:(id<NSCopying>)aKeyvoid 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 `{}`.

其次,你应该知道目标设备的字节顺序。

您可以通过以下代码进行测试。

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

在我的 x86-64 设备中,它的输出是 4 3 2 1

因此,当我更改 has_assoc 时,最后一个字节将发生变化。

在指针中,它将add/minus 2

isa: 0x1dffff795ca0f1 

isa: 0x1dffff795ca0f3 

对于isa_t,您可以使用下面的代码得到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;
}

您在模拟器上进行了测试。您链接的文章是这样说的:

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

您必须在设备上进行测试才能看到非指针 isa