在我的案例中使用未声明的标识符错误

Use of undeclared identifier error in my case

我的代码调用了一个 C 库函数:

@implementation Store
  ...
  -(void) doWork {
    // this is a C function from a library
    int data = getData(); 
    ...
  }
end

我正在对上述函数进行单元测试,我想在我的测试中模拟 C 函数 getData(),这是我的测试用例:

@interface StoreTests : XCTestCase {
    int mData;
    Store *store;
}
@end

@implementation StoreTests

-(void) setUp {
  [super setUp];
   mData = 0;
   store = [[Store alloc] init];
}

-(void) testDoWork {
  // this call will use the mocked getData(), no problem here.
  [store doWork];
}

// mocked getData()
int getData() {
   mData = 10; // Use of undeclared identifier 'mData', why?
   return mData;
}

...
@end

为什么我会收到编译器错误: Use of undeclared identifier 'mData' 内部模拟 getData() 函数?

我为我的问题找到了一个解决方案,即在 @interface StoreTests : XCTestCase 上方声明 mData,如下所示:

int mData;
@interface StoreTests : XCTestCase {
    Store *store;
}
@end
...

您误解了实例方法和变量的工作原理。

每个实例方法都有一个引用当前实例(或"current object")的变量self,并且使用实例变量,例如mData,是shorthand 用于使用 self 访问该变量,例如 self->mData,其中 -> 是用于字段访问的 (Objective-)C 运算符。所以你写的setup方法"long hand"是:

-(void) setUp {
   [super setUp];
   self->mData = 0;
   self->store = [[Store alloc] init];
}

但是 self,对实例的引用,本身是从哪里来的呢?好吧,它并不神奇,只是隐藏了,它作为隐藏的额外参数自动传递给实例方法。 此时切换到伪代码来显示这一点。您的 setup 方法有效地 编译为:

-(void) setUp withSelf:(StoreTest *)self {
   [super setUp];
   self->mData = 0;
   self->store = [[Store alloc] init];
}

和一个电话,例如:

StoreTests *myStoreTests = ...

[myStoreTests setup];

被有效地编译成类似这样的东西:

[myStoreTests setup withSelf:myStoreTests];

自动添加额外的 self 参数。

现在以上所有仅适用于方法,并使它们能够访问实例变量和方法,不适用于普通 C 函数 - 它们没有隐藏 self 参数,不能访问实例变量。

您在您添加的关于在界面外声明 mData 的答案中提到的解决方案:

int mData;
@interface StoreTests : XCTestCase {
   Store *store;
}
@end

mData 更改为 全局变量 ,而不是 实例变量 。 C 函数可以访问全局变量。然而,这确实意味着 class 的每个实例都共享相同的 mData,在这种情况下只有一个 mData 而不是每个实例都有一个。

因此,将一个实例变量变成一个全局变量并不是解决此类问题的通用方法,但是因为您不太可能拥有多个 StoreTests class 它的实例在这种情况下是合适的解决方案。

但是你应该做一个改变:在一个程序中你只能有一个具有给定名称的全局变量,所以你的 mData 必须是唯一的并且可以被 any[=68 访问=] 代码,而不仅仅是 StoreTests 的代码。您可以通过将变量声明为 static:

来缓解这种情况
static int mData;

这将变量保持为全局变量,但仅使其对与声明相同的 文件 中的代码可见,这可能只是 StoreTests 的代码。

HTH