代码之家  ›  专栏  ›  技术社区  ›  Ben Packard Ross Knipe

(不是这样)在使用属性时出现愚蠢的objective-c继承问题-gcc bug?

  •  13
  • Ben Packard Ross Knipe  · 技术社区  · 16 年前

    更新-许多人坚持我需要为该属性声明一个IVAR。有些人不是这样说的,因为我使用的是现代运行时(64位)。我可以确认我已经成功地使用了@property而没有ivars几个月了。因此,我认为“正确”的答案是一个解释,为什么在64位上,当(并且仅当)我要从子类访问IVAR时,我突然必须显式地声明它。到目前为止,我看到的唯一一个可能的gcc错误(谢谢yuji)。毕竟不是那么简单…为了澄清可能存在的错误:当从基类继承时,如果子类在访问IVAR之前碰巧使用@synthesis实现了一个不相关的访问器,那么子类就不能访问父类的IVAR。

    我已经用这个挠头好几个小时了——我没有太多地使用继承权。

    在这里,我建立了一个简单的测试B类,它继承自测试A,在这里声明了一个IVAR。但我得到一个编译错误,即变量是未声明的。只有当我添加属性和合成声明时才会发生这种情况-没有它们就可以正常工作。

    TestA Header:

    #import <Cocoa/Cocoa.h>
    @interface TestA : NSObject {
        NSString *testString;
    }
    @end
    

    测试实现为空:

    #import "TestA.h"
    @implementation TestA  
    @end
    

    TestB Header:

    #import <Cocoa/Cocoa.h>
    #import "TestA.h"
    @interface TestB : TestA {
    }
    @property (nonatomic, retain) NSString *testProp;
    @end
    

    testb实现(错误-“teststring”未声明)

    #import "TestB.h"
    @implementation TestB
    @synthesize testProp;
    - (void)testing{
        NSLog(@"test ivar is %@", testString);
    }
    @end
    
    4 回复  |  直到 15 年前
        1
  •  10
  •   Yuji    16 年前

    我认为这是GCC4.2.1的缺陷。 我做了这个文件 foo.m 与内容

    #import <Foundation/Foundation.h>
    @interface TestA : NSObject {
        NSString *testString;
    }
    @end
    
    @implementation TestA  
    @end
    
    @interface TestB : TestA {
    }
    @property (retain) NSString *testProp;
    @end
    
    @implementation TestB
    @synthesize testProp;
    - (void)testing{
    NSLog(@"test ivar is %@", testString);
    }
    @end
    

    注意,在64位模式下,可以忽略实例变量。 我在OS X 10.6.3上的GCC 4.2.1给出了一个错误:

    $ gcc -arch x86_64 -c foo.m
    aho.m: In function ‘-[TestB testing]’:
    aho.m:19: error: ‘testString’ undeclared (first use in this function)
    aho.m:19: error: (Each undeclared identifier is reported only once
    aho.m:19: error: for each function it appears in.)
    

    这个编译没有问题,通过更改

    NSLog(@"test ivar is %@", testString);
    

    NSLog(@"test ivar is %@", self->testString);
    

    Clang编译它没有任何问题。

    (在32位模式下,我

    $ gcc -arch i386 -c foo.m
    aho.m:17: error: synthesized property ‘testProp’ must either be named 
    the same as a compatible ivar or must explicitly name an ivar
    aho.m: In function ‘-[TestB testing]’:
    aho.m:19: error: ‘testString’ undeclared (first use in this function)
    aho.m:19: error: (Each undeclared identifier is reported only once
    aho.m:19: error: for each function it appears in.)
    

    正如曼朱纳特所写,这是一种完全可以预料的行为。)

    然而 我认为访问超类的实例变量通常是一个相当糟糕的主意:当您实现方法超类时,不能对实例变量进行任何假设,因为子类可能以最糟糕的方式对它进行了调整。您至少需要写下允许或不允许对实例变量执行哪种操作…记住,您可能需要将代码维护多年!我更喜欢在方法和属性级别上保持代码各个部分之间的编程契约。

    最后你应该改变

    @property NSString *testProp;
    

    @property (copy) NSString *testProp;
    

    或者至少

    @property (retain) NSString *testProp;
    

    如果您没有在OS X上使用GC,否则exp-bad-access将等待您!

        2
  •  1
  •   Andy White    16 年前

    我想你只是有个打字错误-应该是“teststring”而不是“test”

        3
  •  1
  •   Quinn Taylor    16 年前

    我在看 error: 'testString' undeclared (first use in this function) @synthesize 就在 testing 方法。如果我移动 @合成 方法实现下面。这可能是因为testb类没有 testProp 用于声明属性的字符串实例变量。(在旧版(32位)运行时中, 必须 声明实例变量,以便在现代运行时(64位Mac、iPhone)中用于属性_?,这些变量是可以推断的,因此声明它们是可选的。)是否可能要命名该属性? testString 相反?


    编辑: 在GCC4.2中,如果您将testb.h更改为以下内容,它将起作用:

    #import "TestA.h"
    
    @interface TestB : TestA {
        NSString *testProp; // <-- Adding this fixes the errors
    }
    @property NSString *testProp;
    
    @end
    

    但是,使用clang llvm编译器,代码工作时不会被修改。也许这是一个要归档的bug。

        4
  •  1
  •   terrinecold    15 年前

    我刚刚遇到了同样的问题,但是来源非常复杂,我不太明白当我使用gcc时,是什么让父级的ivar无法访问。我只知道几个月前,在我的代码发生变化之前,它是有效的,而且它是与clang一起工作的,我已经用了一段时间了。突然间,我不得不用GCC来建造,但它不会再有了。

    至少这篇文章给了我一个旁路(声明IVARS)。我很惊讶Xcode的最新版本没有包含一个固定的编译器