Reputation: 12842
I'm having a problem with the behavior of a static
variable defined in global scope in an Objective-C .m file. Specifically, I'm seeing different instances of the object referenced by the same variable from the same code depending on the scope when executing from an XCTest target.
How does a global static variable, defined in a .m file, behave between the main and XCTest targets? Here's an example of the problem I'm seeing:
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
Here's an extremely simple ViewController
:
- (void)viewDidLoad {
[super viewDidLoad];
Manager *tempManager = [[Manager alloc] init];
[Manager setManager:tempManager];
}
I'm attempting to write XCTest unit tests that leverage Manager
. The thing is, I'm seeing different instances of Manager
from within the same flow of code execution depending on the context of the code. This was totally new and weird to me. For example, consider this unit test:
- (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]);
}
Here's some observed behavior:
po [Manager sharedManager]
I will see a n object instance with a memory address. I'm assuming that is because ViewController
is the initial view controller for the project's storyboard, and viewDidLoad()
creates and sets the first Manager
shared instance.[po Manager sharedManager]
a different object instance is printed than the NSLog at this line prints, but the same instance as printed at the breakpoint at 1.viewDidLoad()
runs an creates a new Manager
. Breaking on this line with a po
shows a new instance, but the NSLog
prints the same instance as 3.Important point Often the memory address of a NSLog
'd object is different from the debugger po
'd object. I don't know why. I'm guessing it is related to how XCTest executes in a different "app" instance?
The behavior I'm observing is that within the same flow of code, access of a static
global variable within a .m file varies depending on which file is accessing it. Why?
I posted a fully functional, bare bones, project demonstrating this on GitHub at: https://github.com/obuseme/TestStatic
Upvotes: 4
Views: 1185
Reputation: 20980
When I tried recreating the problem by hand in a fresh project, I got:
tempmanager
.viewDidLoad
.This is as expected. Your different results suggests that there's something strange in your project settings. I then downloaded your project and reproduced what you described. An important warning appears in the log:
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.
Here's the problem:
There are two occurrences of Manager.m. Each has its own copy of the sharedManager
static variable.
Upvotes: 2