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

How do static variables behave between multiple targets?

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

.m 文件中定义的全局静态变量在主目标和 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 单元测试。问题是,根据代码的上下文,我在同一代码执行流中看到 不同的 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 在此行打印,但与在断点 1 处打印的实例相同。
  4. viewDidLoad() 运行并创建一个新的 Manager。用 po 打破这一行显示一个新实例,但 NSLog 打印与 3.
  5. 相同的实例

要点 通常 NSLog 对象的内存地址与调试器 po 对象的内存地址不同。我不知道为什么。我猜这与 XCTest 在不同 "app" 实例中的执行方式有关?

我观察到的行为是,在相同的代码流中,.m 文件中 static 全局变量的访问因访问它的文件而异。为什么?

我在 GitHub 的 https://github.com/obuseme/TestStatic

上发布了一个功能完备的基本项目来演示这一点

当我尝试在新项目中手动重现问题时,我得到:

  1. 通过加载初始视图控制器创建的管理器。 (顺便说一下,这是一种测试气味,因为测试应该完全控制它们的环境。对于单元测试,我使用 separate application delegate 来防止这种情况发生。)
  2. 记录了同一位管理员。
  3. tempmanager.
  4. 管理员由viewDidLoad设置。

这符合预期。您的不同结果表明您的项目设置中存在一些奇怪的东西。然后我下载了您的项目并复制了您所描述的内容。日志中出现重要警告:

objc[5304]: Class Manager is implemented in both /Users/jorei/Library/Developer/CoreSimulator/Devices/BEEDA9FD-5FDA-4347-8691-FD80B8C7A18D/data/Containers/Bundle/Application/020A6698-99B6-472A-8E77-330CBCB5AA1A/TestStatic.app/TestStatic and /Users/jorei/Library/Developer/Xcode/DerivedData/TestStatic-clulxcackwiypobvsqvfatqiznvi/Build/Products/Debug-iphonesimulator/TestStatic.app/PlugIns/TestStaticTests.xctest/TestStaticTests. One of the two will be used. Which one is undefined.

这是问题所在:

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