【问题标题】:When to use id in objective-c? [duplicate]什么时候在objective-c中使用id? [复制]
【发布时间】:2015-07-31 05:45:25
【问题描述】:

我一直在阅读和了解instancetype 以及在大多数情况下应该如何在现代objective-c 中使用它而不是id。那么,我可以问一下,何时实际使用id 而不是instancetype 是可取的吗? 谢谢。

【问题讨论】:

  • 还有一个好消息,这比上面的链接更合适:stackoverflow.com/a/14652187/3402095
  • 是的,我刚刚读过 Whirlwind,但我需要 id 用例来完善我所学的内容。

标签: objective-c


【解决方案1】:

id

id 是泛型类型变量。 id在编译时不会警告我们,但是如果有问题就会崩溃。

实例类型

instancetype 在编译时为我们进行类型检查以警告我们出现问题。

例如:

Animal.h

@interface Animal : NSObject

+ (id)giveMeAnimalA;
+ (instancetype)giveMeAnimalB;
+ (Animal *)giveMeAnimalC;

@end

Animal.m

@implementation Animal

+ (id)giveMeAnimalA {
    return [[[self class] alloc] init];
}

+ (instancetype)giveMeAnimalB {
    return [[[self class] alloc] init];
}

+ (Animal *)giveMeAnimalC {
    return [[[self class] alloc] init];
}

@end

假设如果我们使用[[Animal giveMeAnimalA] count];

编译器不会警告我们,但我们会在运行时崩溃并出现异常,因为 Animal 没有 count 方法。

如果我们使用[[Animal giveMeAnimalB] count];

编译器会立即警告我们 Animal 没有 count 方法,我们可以避免在运行时崩溃。但是让我们的返回类型 Animal* 不是更简单吗?

假设我们有一个Dog 的子类Animal

@interface Dog : Animal

- (void)makeSound;

@end

现在如果我们尝试调用

[[Dog giveMeAnimalC] makeSound];

这行不通,因为我们会返回一个没有 makeSound 方法的 Animal

【讨论】:

    【解决方案2】:

    对于完整的最后一个答案,我建议您提供一个支持 Id 的示例。它在 ForIn 循环上(快速枚举)

    想象一下,你有一个包含三个不同对象的数组,如下所示:

        NSArray *anotherArray = @[@"One element of Another Array",@"Second Element of Another Array"];
        NSArray *array = @[@"First",@[anotherArray],@(12)];
    
        for (id item in array)
        {
            if ([item isKindOfClass:[NSString class]])
            {
                NSLog(@"Im a NSString");
            }
    
            if ([item isKindOfClass:[NSArray class]])
            {
                NSLog(@"Im a NSArray");
            }
    
            if ([item isKindOfClass:[NSNumber class]])
            {
                NSLog(@"Im a NSNumber");
            }
    
    
        }
    

    【讨论】:

    • 非常有用的例子,谢谢。
    【解决方案3】:

    “我一直在阅读和学习 instancetype 以及在大多数情况下应该如何在现代 Objective-c 中使用它而不是 id。我能问一下,什么时候实际使用 id 而不是实例类型?谢谢。”

    你学错了。的种类。问题是像Objective-C这样的语言很复杂,每条规则都会附带一长串“如果a和b和c做这个”......你必须理解。

    instancetype 用于一种非常特殊的情况:作为 init 方法的返回类型。例如,您不能使用 UIButton*,因为子类可以使用 UIButton 的 init 方法,因此 init 方法实际上不是 UIButton 而是某个子类。这就是为什么使用“id”的原因,它的意思是“某个对象,但我不知道实际上是哪个对象”。另一方面,“instancetype”告诉编译器“你很聪明,你知道了。所以使用 [[UIButton alloc] init] 编译器知道它返回 UIButton*。[[MyButtonSubclass alloc] init] 编译器知道它返回 MyButtonSubclass *。

    在任何其他情况下,您都不会使用 instancetype。

    始终向编译器提供尽可能多的信息。如果您有一个声明为 UIButton* 的对象,编译器就会知道它是 UIButton 还是子类。如果你有一个声明为 id 的对象,编译器一无所知。这意味着编译器无法告诉您您是否做了一些愚蠢的事情(例如将 UIButton* 分配给 NSString*,或在 UIButton 上调用 length 方法)。

    【讨论】:

      【解决方案4】:

      如果事先不知道返回值的类型,则不能使用instancetype 作为返回类型。如果某个方法可能根据上下文返回NSButtonNSString,则只能使用id

      instancetype 只是使用它的类的占位符;如果class Foo 的方法是这样的

      - (instancetype) getMeFoo
      

      那么就等价于

      - (Foo *) getMeFoo
      

      它不能返回NSString;编译器会抱怨。不过,

      - (id) getMeFoo
      

      可以返回任何类类型。

      理论上,您可以使用可能返回类型的公共超类(例如,NSObject);但是在分配给具体变量时您需要对其进行类型转换,否则编译器会警告您。

      - (NSObject *) getMeFoo {
          return @"foo!";
      }
      NSString *myString = (NSString *)[self getMeFoo];
      

      id 类型是“自动”转换:

      - (id) getMeFoo {
          return @"foo!";
      }
      NSString *myString = [self getMeFoo];
      

      但永远不要忘记检查你是否真的得到了预期的类型:

      NSString *myString = [self getMeFoo];
      if (![myString isKindOfClass:[NSString class]]) {
          // Danger, Will Robinson!
      }
      

      【讨论】:

        【解决方案5】:

        id 是一种通用数据类型,可以保存任何类型的数据,如 nsstring、uiimage、nsarray 和其余所有数据,因此如果您有从方法动态返回对象等要求,则最好使用该方法的返回类型方法作为id,希望你能得到它

        【讨论】:

          猜你喜欢
          • 2011-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-26
          • 1970-01-01
          • 2011-06-16
          • 2010-09-11
          相关资源
          最近更新 更多