在我的案例中使用未声明的标识符错误
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
我的代码调用了一个 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