获取nil对象的类类型?

时间:2013-03-23 14:58:22

标签: objective-c

如果我有一个已经分配的对象,那么执行object.class会返回一个非零值。到现在为止还挺好。但是,如果尚未分配对象,则访问object.class会返回nil

我想动态地根据其类型分配对象,例如:

@property NSArray *myArray;
...

// myArray is nil so far

self.myArray = [_myArray.class new];

但是,我不能这样做因为_myArray.class返回nil。那么我如何确定一个nil实例的类类型呢?

更新 事实上这是可能的。查看下面的answer

5 个答案:

答案 0 :(得分:4)

您无法确定nil实例的类,因为它没有实例:从字面上看,它可以是从变量类型派生的任何类型。例如,NSMutableArrayNSArray完全兼容:

NSArray *myArray = [NSArray new]; // OK
NSArray *myArray = [NSMutableArray new]; // Also OK

由于不同子类的运行时功能可能会有很大差异,因此您的程序始终可以决定它想要的对象类型。

答案 1 :(得分:3)

Objective-C是一种鸭式语言。这意味着你可以做或不可以做几件事,而你不能做的事情之一是静态地获得对变量类型的引用。

具体来说,在你的表达中:

[_myArray.class new]

首先,评估_myArray.class,然后向结果发送new消息。由于_myArray开始为nil_myArray.class也会返回nilnew消息也将返回nil,因为发送任何内容消息到nil返回nil(或返回类型的最接近的表示为零)。这就是为什么它不起作用。

我怀疑你来自像C#这样的强类型语言;你现在正在做的是相当于Foo foo = (Foo)Activator.CreateInstance(foo.GetType()),它肯定会失败,因为foo.GetType()将不会编译或抛出异常(取决于它是一个类字段还是局部变量),因为它从未被赋予价值。在Objective-C中,它编译但不起作用。你想要的是Activator.CreateInstance(typeof(Foo)),但请注意Foo现在也是硬编码的,所以你也可以创建一个new Foo()

你说编译器“知道对象的类型”。这不完全正确。首先,NSArrayNSMutableArrayNSArray类群集的根类。这意味着两者都是抽象的,[NSArray alloc][NSMutableArray alloc]上次检查时返回子类的实例(NSCFArray,可能还有别的东西;我记得看到_NSArrayM) 。也许[NSArray new]有效,但它没有给你一个简单的NSArray

其次,未强制执行类型安全。请考虑以下代码:

id foo = @"foo";
NSArray* bar = foo; // no warning!

因此即使编译器认为barNSArray,它实际上也是NSString。如果我们插入您的代码:

id foo = @"foo";
NSArray* bar = foo; // no warning!
NSArray* baz = [bar.class new];

baz现在也是NSString。由于您要求bar的运行时类,编译器与操作无关。

正是由于这种行为,您可能应该使用知道的类来实例化您的对象,使用[NSArray new]而不是信任_myArray为非nil,并且是你认为的。

答案 2 :(得分:1)

你必须初始化属性,否则它将为nil,向nil对象发送消息,它将返回nil,因此,你必须首先启动数组,如_array = [[NSArray alloc] init];

答案 3 :(得分:0)

所以,对于任何想知道这是否可能的人来说,它是:

objc_property_t property = class_getProperty(self.class, "myArray");
const char * const attrString = property_getAttributes(property);
const char *typeString = attrString + 1;
const char *next = NSGetSizeAndAlignment(typeString, NULL, NULL);
const char *className = typeString + 2;
next = strchr(className, '"');
size_t classNameLength = next - className;
char trimmedName[classNameLength + 1];

strncpy(trimmedName, className, classNameLength);
trimmedName[classNameLength] = '\0';
Class objectClass = objc_getClass(trimmedName);

NSLog(@"%@", objectClass);

输出:

  

的NSArray

extobjc的帮助下完成。

答案 4 :(得分:-1)

Nil没有班级类型

在Objective-C中,实例变量的实际类仅在运行时确定。所以,你无法知道一个零对象的类。

在您的情况下,这不是问题,因为您只需要这样做:

NSArray *myArray = [NSArray new];

或者

NSArray *myArray = [[NSArray alloc] init];

在Objective-C中,大多数决策都推迟到运行时

(尽可能多)

  

Objective-C是一种面向运行时的语言,这意味着它就是它   可能它会推迟决定实际执行的内容   编译&将实际执行的时间链接到运行时

     

这为您提供了很多灵活性,可以重定向消息   根据需要使用适当的物体,或者您甚至可以故意使用   交换方法实现等。

     

这需要使用运行时   它可以反省物体,看看它们做了什么。不要回应   和适当的派遣方法。如果我们将其与语言进行对比   像C.在C中你开始使用main()方法,然后从那里开始   它几乎是一个遵循你的逻辑和自上而下的设计   在编写代码时执行函数。 C结构不能   转发请求以执行其他目标的功能。

来源:Understanding the Objective-C Runtime