(Не так) Глупая проблема наследования Objective-C при использовании свойства - Ошибка GCC? - PullRequest
13 голосов
/ 06 мая 2010

Обновление - Многие люди настаивают на том, что мне нужно объявить iVar для этого свойства. Некоторые говорят, что не так, как я использую Modern Runtime (64 бит). Я могу подтвердить, что успешно использую @property без iVars уже несколько месяцев. Поэтому я думаю, что «правильный» ответ - это объяснение того, почему на 64-битной системе мне вдруг приходится явно объявлять iVar, когда (и только когда) я собираюсь получить к нему доступ из дочернего класса. Единственный, кого я видел до сих пор, - это возможная ошибка в GCC (спасибо Yuji). В конце концов, не все так просто ... Чтобы прояснить возможную ошибку, можно сделать следующее: при наследовании от базового класса дочерний элемент не может получить доступ к iVar родителя, ЕСЛИ этот дочерний объект также реализует НЕДОПУСТИМОЙ аксессор, используя @synthesize ДО ДО доступа к iVar.

Я пару часов царапал голову этим - я не слишком много использовал наследование.

Здесь я настроил простой класс Test B, который наследуется от Test A, где объявлен ivar. Но я получаю ошибку компиляции, что переменная необъявлена. Это происходит только тогда, когда я добавляю свойство и синтезирую объявления - прекрасно работает без них.

Заголовок TestA:

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

TestA Реализация пуста:

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

Заголовок TestB:

#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 ]

10 голосов
/ 06 мая 2010

Я думаю, что это ошибка GCC 4.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-битном режиме можно пропустить переменную экземпляра.Мой GCC 4.2.1 на OS X 10.6.3 дал мне ошибку:

$ 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;

, если вы не используете GC на OS X. В противном случае EXP_BAD_ACCESS будет ждать вас!

1 голос
/ 31 марта 2011

Я просто столкнулся с той же проблемой, но источники были достаточно сложными, и я не совсем понял, что сделало iVar от родительского недоступным, когда я использовал GCC. Я только знал наверняка, что несколько месяцев назад и до изменений в моем коде он работал, а также, что он работал с Clang, который я использовал некоторое время. Внезапно мне пришлось строить с GCC, и это больше не будет.

По крайней мере, эта статья дала мне обход (объявите iVars). Я удивлен, что последняя версия XCode не включает фиксированный компилятор

1 голос
/ 06 мая 2010

Я вижу error: 'testString' undeclared (first use in this function), когда @synthesize прямо перед testing методом. Ошибка исчезнет, ​​если я переместу @synthesize ниже реализации метода. Это может быть связано с тем, что у класса TestB нет строковой переменной экземпляра testProp для использования с объявленным свойством. (В устаревшей (32-разрядной) среде выполнения вы должны объявлять переменные экземпляра для использования в свойствах - в современной среде выполнения (64-разрядный Mac, iPhone) они могут быть выведены, поэтому их объявлять необязательно) Возможно ли, что вы хотели назвать свойство testString вместо этого?


РЕДАКТИРОВАТЬ: В GCC 4.2 это работает, если вы измените TestB.h на следующее:

#import "TestA.h"

@interface TestB : TestA {
    NSString *testProp; // <-- Adding this fixes the errors
}
@property NSString *testProp;

@end

Однако, используя компилятор Clang-LLVM, код работает без изменений. Возможно, это ошибка в файле.

1 голос
/ 06 мая 2010

Я думаю, что вы просто опечатка - это должно быть "testString", а не "test"

...