假设我在-(void) doSomething
类的m文件中定义了一个私有方法School
(它未在接口头中声明):
@implementation School
-(void) doSomething {
...
}
@end
我有School
类的单元测试用例:
@interface SchoolTest : XCTestCase
@end
@implementation SchoolTest
- (void)testExample {
id mySchoolMock = [OCMockObject partialMockForObject:[School new]];
// I know I can't access '-(void)doSomething' since it is a private method
}
@end
我知道我通常无法访问此私有方法。但如果我在我的测试类中重新声明-(void) doSomething
如下:
@interface SchoolTest : XCTestCase
// re-declare it in my test class
-(void) doSomething
@end
@implementation SchoolTest
- (void)testExample {
id mySchoolMock = [OCMockObject partialMockForObject:[School new]];
// Now I can access '-(void)doSomething'!!! Why now I can access the private method in `mySchool` Instance in my test class ?
[mySchoolMock doSomething];
}
@end
为什么以上述方式我可以使用School
实例访问mySchool
类的私有方法?这背后的客观理论是什么?
(我这样做是因为我已经阅读了this question的答案,但我不明白为什么我们能做到这一点?背后的理论是什么?)
答案 0 :(得分:1)
Objective-C缺乏在运行时处于活动状态的方法和字段的保护机制。当您在.m
文件中隐藏该方法以使其“私有”时,所有“保护”都在编译时完成。
编译方法后,其信息存储在一个表中,该表用于在运行时调度方法调用。拥有方法选择器的任何人都可以调用它,就好像它是公共的一样。这在Objective-C中实现了很多动态行为。
不幸的是,运行时没有选择器的保护。当您重新声明私有方法进行测试时,您实际上是在告诉Objective-C您知道该方法存在,并且坚持让您拨打电话。
执行此操作时可能会发生两件事:如果方法实际存在,则调用将成功。但是,如果该方法不存在,则最终会在调用无效选择器时崩溃。
该方法在我的测试类中重新声明,而不是在学校类
中
这部分有点棘手。 id
是一种特殊类型,它允许您使用非常少的编译时检查来调用任何方法。基本上,编译器会验证是否存在任何知道具有匹配签名的方法的类型,然后它允许您进行调用。这类似于为您的方法创建@SELECTOR
,然后在id
类型的对象上动态调用它。基本上,编译器验证器没有明显错误,并让调用继续进行。
答案 1 :(得分:0)
值得注意的是,你没有重新宣布"方法 - 您向doSomething
类添加了SchoolTest
方法。这与链接答案中提到的内容大不相同 - 有一个类别用于显示"私人"测试类的方法。所以这样的事情会更好:
@interface School (Tests)
- (void)doSomething;
@end
你做了什么,因为mySchoolMock
属于id
类型,而doSomething
是一个可见的符号,所以Xcode和编译器并没有警告你任何事情,它因为@dasblinkenlight解释的机制在运行时完成了它们的工作。