ios - 静态变量在多个目标之间的行为如何?

标签 ios objective-c iphone xcode xctest

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

.m 文件中定义的全局静态变量在主目标和 XCTest 目标之间的行为如何?这是我遇到的问题的示例:

设置代码

经理.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 是项目 Storyboard的初始 View Controller ,而 viewDidLoad() 创建并设置第一个 Manager 共享实例。
  2. 此行将 null 打印到控制台,这很奇怪,因为 1 上的断点和 po 在控制台中显示了实际对象。
  3. 此行打印一个实际的对象实例,但与第 1 行不同实例。有趣的是,如果我在这一行中断,则会出现 [po Manager sharedManager]打印的对象实例与此行打印的 NSLog 不同,但与 1 处断点处打印的实例相同。
  4. viewDidLoad() 运行并创建一个新的Manager。使用 po 中断此行会显示一个新实例,但 NSLog 会打印与 3 相同的实例。

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

我观察到的行为是,在同一代码流中,.m 文件中的 static 全局变量的访问会根据访问它的文件而有所不同。为什么?

我在 GitHub 上发布了一个功能齐全、简单的项目来演示这一点:https://github.com/obuseme/TestStatic

最佳答案

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

  1. 通过加载初始 View Controller 创建的管理器。 (顺便说一句,这是一个测试气味,因为测试应该完全控制其环境。对于单元测试,我使用 separate application delegate 来防止这种情况发生。)
  2. 登录的是同一个经理。
  3. 临时管理器
  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.

问题是这样的:

Incorrect Target Membership

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

关于ios - 静态变量在多个目标之间的行为如何?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38616990/

相关文章:

ios - 捕获 "The In-App Purchase has already been bought"事件

ios - 类型 'IntentHandler' 不符合协议(protocol) 'INStartAudioCallIntentHandling'

iphone - 如何使 UIView 中的内容可滚动?

iphone - xcodebuild安装路径?

iphone - 归档有关符号链接(symbolic link)的 iOS 项目时出错

ios - UIImageView 中的渐变/阴影,如 Flipboard

ios - swift 3 iTunes订阅收据验证实时模式

iphone - 如何检测在 iphone 上加载 UIWebView 时发生的 url 重定向?

iphone - NSTimezone没有提供GMT +或GMT-甲酸酯iphone?

ios - 如何将应用商店应用链接添加到应用内,而不是打开 "app store.app"?