静态变量如何在多个目标之间表现?
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 行断点暂停。如果我
po [Manager sharedManager]
我会看到一个带有内存地址的 n 对象实例。我假设这是因为 ViewController
是项目故事板的初始视图控制器,并且 viewDidLoad()
创建并设置第一个 Manager
共享实例。
- 这一行将 null 打印到控制台,这很奇怪,因为 1 上的断点和 po 在控制台中显示了一个实际对象.
- 这一行打印了一个实际的对象实例,但是与第 1 行 不同的 实例。有趣的是,如果我在这一行打断
[po Manager sharedManager]
一个不同的实例打印的对象实例比 NSLog 在此行打印,但与在断点 1 处打印的实例相同。
viewDidLoad()
运行并创建一个新的 Manager
。用 po
打破这一行显示一个新实例,但 NSLog
打印与 3. 相同的实例
要点 通常 NSLog
对象的内存地址与调试器 po
对象的内存地址不同。我不知道为什么。我猜这与 XCTest 在不同 "app" 实例中的执行方式有关?
我观察到的行为是,在相同的代码流中,.m 文件中 static
全局变量的访问因访问它的文件而异。为什么?
我在 GitHub 的 https://github.com/obuseme/TestStatic
上发布了一个功能完备的基本项目来演示这一点
当我尝试在新项目中手动重现问题时,我得到:
- 通过加载初始视图控制器创建的管理器。 (顺便说一下,这是一种测试气味,因为测试应该完全控制它们的环境。对于单元测试,我使用 separate application delegate 来防止这种情况发生。)
- 记录了同一位管理员。
tempmanager
.
- 管理员由
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
静态变量副本。
我在 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 行断点暂停。如果我
po [Manager sharedManager]
我会看到一个带有内存地址的 n 对象实例。我假设这是因为ViewController
是项目故事板的初始视图控制器,并且viewDidLoad()
创建并设置第一个Manager
共享实例。 - 这一行将 null 打印到控制台,这很奇怪,因为 1 上的断点和 po 在控制台中显示了一个实际对象.
- 这一行打印了一个实际的对象实例,但是与第 1 行 不同的 实例。有趣的是,如果我在这一行打断
[po Manager sharedManager]
一个不同的实例打印的对象实例比 NSLog 在此行打印,但与在断点 1 处打印的实例相同。 viewDidLoad()
运行并创建一个新的Manager
。用po
打破这一行显示一个新实例,但NSLog
打印与 3. 相同的实例
要点 通常 NSLog
对象的内存地址与调试器 po
对象的内存地址不同。我不知道为什么。我猜这与 XCTest 在不同 "app" 实例中的执行方式有关?
我观察到的行为是,在相同的代码流中,.m 文件中 static
全局变量的访问因访问它的文件而异。为什么?
我在 GitHub 的 https://github.com/obuseme/TestStatic
上发布了一个功能完备的基本项目来演示这一点当我尝试在新项目中手动重现问题时,我得到:
- 通过加载初始视图控制器创建的管理器。 (顺便说一下,这是一种测试气味,因为测试应该完全控制它们的环境。对于单元测试,我使用 separate application delegate 来防止这种情况发生。)
- 记录了同一位管理员。
tempmanager
.- 管理员由
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
静态变量副本。