静态变量如何在多个目标之间表现?

时间:2016-07-27 15:13:07

标签: ios objective-c iphone xcode xctest

我对Objective-C .m文件中全局范围中定义的static变量的行为有疑问。具体来说,我从同一代码中看到同一个变量引用的对象的不同实例,具体取决于从XCTest目标执行时的范围。

.m文件中定义的全局静态变量如何在main和XCTest目标之间运行?以下是我看到的问题的一个例子:

设置代码

Manager.m

#import "Manager.h"

// This is the variable of interest!
static Manager *sharedManager = nil;

@implementation Manager

+ (instancetype)sharedManager
{
    return sharedManager;
}

+ (void)setManager:(Manager *)manager
{
    sharedManager = manager;
}

@end

这是一个非常简单的ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Manager *tempManager = [[Manager alloc] init];
    [Manager setManager:tempManager];
}

混乱之肉

我正在尝试编写利用Manager的XCTest单元测试。问题是,我在相同的代码执行流程中看到{strong>不同的实例Manager,具体取决于代码的上下文。这对我来说是全新的,也很奇怪。例如,考虑这个单元测试:

- (void)testManager {
    // 1
    ViewController *vc = [[ViewController alloc] init];

    // 2
    NSLog(@"manager %@", [Manager sharedManager]);

    Manager *tempManager = [[Manager alloc] init];
    [Manager setManager:tempManager];

    // 3
    NSLog(@"manager %@", [Manager sharedManager]);
    [vc viewDidLoad];

    // 4
    NSLog(@"manager %@", [Manager sharedManager]);
}

以下是一些观察到的行为:

  1. 第1行的断点暂停。如果我po [Manager sharedManager]我将看到一个带有内存地址的n对象实例。我假设这是因为ViewController是项目故事板的初始视图控制器,viewDidLoad()创建并设置了第一个Manager共享实例。
  2. 此行将 null 打印到控制台,这很奇怪,因为 1 上的断点和po在控制台中显示了一个实际对象。
  3. 此行打印一个实际的对象实例,但是与<1>不同的实例。但有趣的是,如果我在此行中断[po Manager sharedManager],则打印的对象实例不同于此行中的NSLog打印,但与断点处打印的实例相同。
  4. viewDidLoad()运行会创建一个新的Manager。使用po打破此行会显示一个新实例,但NSLog会将同一个实例打印为3。
  5. 重要事项 NSLog'd对象的内存地址通常与调试器po'd对象不同。我不知道为什么。我猜它与XCTest在不同的“app”实例中的执行方式有关吗?

    我观察到的行为是,在同一代码流中,.m文件中static全局变量的访问权限取决于访问它的文件。为什么呢?

    我在GitHub发布了一个功能齐全,功能齐全的项目:https://github.com/obuseme/TestStatic

1 个答案:

答案 0 :(得分:1)

当我尝试在一个新项目中手动重建问题时,我得到了:

  1. 通过加载初始视图控制器创建的管理器。 (顺便说一句,这是一种测试气味,因为测试应该完全控制他们的环境。对于单元测试,我使用separate application delegate来防止这种情况发生。)
  2. 记录了同一位经理。
  3. tempmanager
  4. viewDidLoad设置的经理。
  5. 这是预期的。您的不同结果表明您的项目设置中存在一些奇怪的内容。然后我下载了你的项目并重现你所描述的内容。日志中会显示一条重要警告:

      

    objc [5304]:类管理器在/ Users / jorei / Library / Developer / CoreSimulator / Devices / BEEDA9FD-5FDA-4347-8691-FD80B8C7A18D / data / Containers / Bundle / Application / 020A6698-99B6-472A中实现-8E77-330CBCB5AA1A / TestStatic.app / TestStatic和/Users/jorei/Library/Developer/Xcode/DerivedData/TestStatic-clulxcackwiypobvsqvfatqiznvi/Build/Products/Debug-iphonesimulator/TestStatic.app/PlugIns/TestStaticTests.xctest/TestStaticTests。将使用两者之一。哪一个未定义。

    问题在于:

    Incorrect Target Membership

    Manager.m有两次出现。每个都有自己的sharedManager静态变量副本。