`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
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>)aKey
和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 `{}`.
其次,你应该知道目标设备的字节顺序。
您可以通过以下代码进行测试。
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
。
我一直认为isa
指针指向实例化的Class。 Class 的 isa
指针指向它的 metaclass
。
但是在一些关于isa
指针介绍的文章中,作者写道:isa
指针中的每一位都可以携带一些关于对象的信息。如下所示:
[objc explain]: Non-pointer 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>)aKey
和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 `{}`.
其次,你应该知道目标设备的字节顺序。
您可以通过以下代码进行测试。
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
。