11

I have a singelton class which will be create in the app delegate.

When i run XCTTests then its get create a second time.

+ (instancetype)urlSchemeManager
{
    static dispatch_once_t onceToken;
    static UrlSchemeManager* _sharedInstance;

    dispatch_once(&onceToken, ^{

        _sharedInstance = [UrlSchemeManager new];


    });
    return _sharedInstance;
}

This is resulting in two different instances. This was no problem if i just use it for unit test. But in the integration test, when i register an observer for urlSchmemeManager i get a EXC_BAD_ACCESS, because it was already observed by the rootViewController (in the UI).

In RootViewController:

UrlSchemeManager * schemeManager = [GlobalSpace globalSpace].urlSchemeManager;
[schemeManager addObserver:self forKeyPath:OBSERVER_KEY_URL_SCHEME_MANAGER_CONTENT_MORE options:NSKeyValueObservingOptionNew context:nil];

Does anyone has an idea how i can get around this problem?

Sam
  • 2,707
  • 1
  • 23
  • 32
  • `when i register an observer vor urlSchmemeManager i get a EXT_BAD_ACCESS` what vor stands for? – KudoCC Jan 09 '14 at 08:17
  • i updated the source code to clear this question. – Sam Jan 09 '14 at 08:19
  • Didn't you mean EXC_BAD_ACCESS? Could you provide more information about this exception (e.g. stack trace) – CouchDeveloper Jan 09 '14 at 08:39
  • 1
    There can be only two distinct instances of `_sharedInstance` IFF there are two distinct instances of `onceToken`. This may happen for example when you have two bundles which each include the module where `onceToken` is defined and these bundles will be linked to one application by the dynamic linker. – CouchDeveloper Jan 09 '14 at 08:58
  • That sounds linke this could be the reason. Do you have an idea how to solve the problme? – Sam Jan 09 '14 at 14:54
  • Well, if the problem genuinely is "library A links to library C + library B links to library C + app links to A & B" the solution is to link all the things in the same link pass, or make sure that all the linkage is dynamic. It's hard to say exactly what's going on here based on only what's posted. – ipmcc Jan 13 '14 at 13:31

2 Answers2

34

I had the same problem with dispatch_once being called multiple times when running a test suite. I fixed it by removing the singleton class from the Target Membership of the Test.

Once you've done that make sure that your test target is dependent on your application in "Build Phases" so that the test still knows about the class.

After that, the test should run and the singleton should only be instantiated one time.

Mike Sabatini
  • 2,541
  • 1
  • 17
  • 10
14

Mike's answer is correct! Just adding some more info. This is a really tricky issue. Seems to be the case that app target and the test target are compiled separated. In runtime, however, the binary for the tests is injected into the app's space. Because it is usually the compiler's job to detect duplicate symbols, and the compilation process is different, it can happen that you have two instances of a class at runtime. Each instance of the class with it's own set of static variables. This is super weird. Tons of weird behaviors can stem from this. Including the double dispatch_once_t execution.

I faced this problems in my cocoapods clases. All pods in your Podfile are, by default, linked into all targets. As a result you will have duplicate clases at runtime when running XCTest. The solution is to specify your pods per target. In my case, for example. I did:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '7.0'

target 'MyApp', :exclusive => true do
    pod 'AFNetworking'
    pod 'ObjectiveRecord', :head
    ...
end

target 'MyApp Tests', :exclusive => true do
  pod 'KIF', '~> 3.0', :configurations => ['Debug']
end

inhibit_all_warnings!

I specifically had issues with the singleton pattern in ObjectiveRecord. Because the core data context manager class was created twice, different sections of my app were not seeing the same data.

fsaint
  • 8,759
  • 3
  • 36
  • 48
  • Thanks, this was exactly my issue :) ( BTW, I don't think you need the two `:exclusive => true`s too.) – orta Oct 29 '15 at 11:08
  • 1
    That's a brilliant answer! – Yuri Solodkin Jul 21 '16 at 11:24
  • I've solved my issue by excluding my singleton from test target compilation. The problem was that it was marked for compilation for both app and unitTest targets. That was the source of duplicated symbols. – Chugaister Sep 02 '16 at 12:32