【问题标题】:Why do I need to cast self to id?为什么我需要将 self 转换为 id?
【发布时间】:2008-11-23 15:17:39
【问题描述】:

我有一个带有 (id) 参数的 init 方法:


    -(id) initWithObject:(id) obj;

我试着这样称呼它:


    [[MyClass alloc] initWithObject:self];

但 XCode 抱怨该参数是“不同的 Objective-C 类型”(这通常表示类型不匹配或间接错误级别)。

如果我明确地将 self 转换为 (id),警告就会消失。在任何一种情况下,代码都会按预期运行。 有趣的是,在下一行中,我将 self 传递给另一个也采用 id 的方法,并且效果很好。

我想知道我是否遗漏了一些微妙的东西 - 还是编译器的特殊性?

在我确定有必要使用它的原因之前,我不太愿意直接施放它。

[编辑]

有人要求我提供更多代码。不确定还有很多其他相关的。这是我进行调用的实际代码。请注意,它本身位于 init 方法中。是对initWithSource 的调用发出警告:


-(id) initWithFrame:(CGRect) frame
{
    self = [super initWithFrame: frame];
    if( self )
    {
        delegate = nil;
        touchDelegate = [[TBCTouchDelegate alloc] initWithSource:self];
        [touchDelegate.viewWasTappedEvent addTarget: self action:@selector(viewWasTapped:)];
    }
    return self;
}

下面是被调用的 init 方法:


-(id) initWithSource:(id) sourceObject
{
    self = [super init];
    if (self != nil) 
    {
        // Uninteresting initialisation snipped
    }
    return self;
}

【问题讨论】:

  • 您可能想在此处发布更多代码;什么都没有跳出来,但只有两条线可以使用,很难说。
  • 同意 Ben 的观点,但要考虑的一件事是声明了 initWithObject 方法吗?
  • 感谢收看,伙计们。为了它的价值,我在上面添加了更多代码。 @Jason:是的,头文件中声明了 initWithSource (我的实际方法名称),它肯定匹配。
  • 哦,为了抢占您的下一个问题 - 是的,我在进行调用的文件中包含该标题 ;-)
  • 您可以创建一个“干净”的例子,而不是使用您现有的项目吗?当我尝试这样做时,我有时会发现问题:)

标签: objective-c casting initialization


【解决方案1】:

通常这意味着在不同的类上有多个 initWithSource: 方法名称,但参数类型相互冲突。请记住,如果变量类型为id,编译器不知道它是什么类。因此,如果您在 id 类型的对象上调用 initWithSource: 并且多个类具有 initWithSource: 方法,则编译器基本上只会选择两者之一。如果它选择了“错误”的那个,那么你会得到一个“distinct Objective-C type”错误。

那么为什么这会发生在你身上?我不是 100% 确定,但请记住 +[TBCTouchDelegate alloc] 返回一个 id。因此链接 alloc/init 调用等价于:

id o = [TBCTouchDelegate alloc];
touchDelegate = [o initWithSource:self];

因此,您在 id 类型的变量上调用 initWithSource:。如果存在冲突的 initWithSource: 方法,您可能会收到此编译器错误。

有冲突的方法吗?我检查了系统,唯一冲突的是NSAppleScript

- (id)initWithSource:(NSString *)source;

现在NSAppleScript Foundation 的一部分,但我注意到这是 iPhone 代码。所以也许你只在为模拟器而不是设备编译时得到这个错误?

无论如何,如果这是您的问题,您可以通过将 alloc/init 拆分到两个不同的行来解决它:

touchDelegate = [TBCTouchDelegate alloc];
touchDelegate = [touchDelegate initWithSource:self];

现在,您在全类型变量上调用 initWithSource:(而不是 id-typed),因此编译器不再需要猜测选择哪个变量。或者你可以从+alloc 投回回报:

touchDelegate = [(TBCTouchDelegate *)[TBCTouchDelegate alloc] initWithSource:self];

另一种解决方案是重命名initWithSource: 以避免冲突,并可能使其更具描述性。你没有说这个类当前的名字是什么,也没有说“源”是什么,所以我不能排除任何可能性。

【讨论】:

  • 我尝试将您的代码复制到我自己的项目中,然后双击 initWithSource。尽管它是一个 iPhone 项目,但根据 Dave 的建议,它确实在尝试引用 NSApplescript。
  • 感谢 Dave - 这很准确 - 并且解释得很清楚!幸运的是,“来源”这个词是相当随意的,所以我可以很容易地改变它。 TouchDelegate 是触摸事件的代理,“源”是它所代理的对象。我现在把它叫做 TouchSource。
  • 也感谢 Ben 的额外确认,并感谢您抽出宝贵时间提供帮助。
  • 这个答案是正确的。它应该被标记为“接受”!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-09
  • 2012-11-23
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 2022-08-03
相关资源
最近更新 更多