Pat
Pat

Reputation: 649

Forward Declaration Stalls App

So I was having issues where I had circular dependencies and so I am trying to implement forward declaration. I have three classes (logger, api, helpers), all of which are singleton objects. I have one controller class (globalclass), that has properties linking to the three classes.

Here is my code:

includes.h:

#import "globalclass.h"
#import "logger.h"
#import "api.h"
#import "helpers.h"

globalclass.h:

@class logger
@class api
@class helpers

@interface globalclass : NSObject

+ (id) sharedGlobal;

- (id) init;

...

@property (nonatomic, strong) logger *log;
@property (nonatomic, strong) api *remote;
@property (nonatmoic, strong) helpers *utils;

...

logger/api/helpers.h templates:

@class globalclass

@interface logger : NSObject

+ (id) sharedLogger;

- (id) init;

...

@property (nonatomic, strong) globalclass *gc;

...

globalclass.m:

#import "includes.h"

@implementation globalclass

/*
 * Singleton Class
 */
+ (id) sharedGlobal
{
    static globalclass *sharedGlobalClass = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedGlobalClass = [[self alloc] init];
    });
    return sharedGlobalClass;
}

/*
 * Init
 */
- (id) init
{
    self = [super init];
    if (!self) return nil;

    // Logging
    self.logger = [logger sharedLogger];

    ...

    return self;
}

logger/api/helpers.m template:

#import "includes.h"

@implementation logger

/*
 * Singleton Class
 */
+ (id) sharedLogger
{
    static logger *sharedLog = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedLog = [[self alloc] init];
    });
    return sharedLog;
}

/*
 * Init
 */
- (id) init
{
    self = [super init];
    if (!self) return nil;

    // Global Class
    self.gc = [GlobalClass sharedGlobal];

    ...

    return self;
}

Then, in my AppDelegate, I have a property to the global class and am doing this:

self.gc = [GlobalClass sharedGlobal];

However, it seems that everytime I boot up my app, it stalls and is automatically terminated because it takes too long to boot up.

Am I thinking about forward globals incorrectly?

The app builds fine.

Any help would be appreciated.

Thanks.

Upvotes: 1

Views: 42

Answers (1)

Mike Mertsock
Mike Mertsock

Reputation: 12015

You have a deadlock, because the dispatch_once block in sharedGlobal is (via the global class init) invoking sharedLogger, which itself is invoking sharedGlobal (via the logger class init).

Since everything is singletons, there are a few approaches you could take to avoid this issue. Here are two:

A. Replace any self.log usages in the global class with [logger sharedLogger], and replace any self.gc usages in the logger class with [globalclass sharedGlobal]. You can get rid of these properties entirely. dispatch_once is very fast after the first invocation, so performance is not a reason to store reference to singletons as properties (though you could conceivably have other reasons)

B. Keep the log and gc properties, but make them readonly properties with custom getters:

@property (nonatomic, readonly) globalclass *gc;
// implementation:
- (globalclass *)gc
{
    return [globalclass sharedGlobal];
}

@property (nonatomic, readonly) logger *log;
// implementation:
- (logger *)log
{
    return [logger sharedLogger];
}

Personally I would choose A.

Both of these alternatives eliminate any references to the singleton accessors from the init methods, which gets rid of the deadlock.

Upvotes: 3

Related Questions