2

如果我有一个已经分配的对象,那么 do 会object.class返回一个非零值。到目前为止,一切都很好。但是,如果尚未分配对象,则访问object.class返回nil

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

@property NSArray *myArray;
...

// myArray is nil so far

self.myArray = [_myArray.class new];

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

更新: 实际上是可能的。在下面查看我的答案

4

5 回答 5

4

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

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

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

于 2013-03-23T15:01:32.963 回答
3

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

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

[_myArray.class new]

首先,_myArray.class被评估,然后将结果发送到new消息中。既然_myArraynil开始,也会_myArray.class返回nil,并且new消息也会返回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!

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

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

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

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

于 2013-03-23T15:06:34.683 回答
1

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

于 2013-03-23T15:03:51.757 回答
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的帮助下完成。

于 2013-03-23T15:51:24.450 回答
-1

Nil 没有类类型

在 Objective-C 中,实例变量上的实际类仅在运行时确定。因此,您无法知道 nil 对象的类别。

这在您的情况下不是问题,因为您只需要执行以下操作:

NSArray *myArray = [NSArray new];

或者

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

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

(越多越好)

Objective-C 是一种面向运行时的语言,这意味着在可能的情况下,它会将关于实际执行什么的决定从编译和链接时间推迟到它实际在运行时执行的时间。

这为您提供了很大的灵活性,您可以根据需要将消息重定向到适当的对象,或者您甚至可以有意交换方法实现等。

这需要使用一个运行时,它可以内省对象以查看它们做什么&不响应和适当地分派方法。如果我们将此与 C 之类的语言进行对比。在 C 中,您从 main() 方法开始,然后从那里它几乎是一个自上而下的设计,在您编写代码时遵循您的逻辑并执行函数。AC struct 无法将请求以执行功能转发到其他目标。

资料来源:了解 Objective-C 运行时

于 2013-03-23T15:04:44.673 回答