0

I've decide to rewrite a java-based game prototype I've been working on in Objective-C. The iOS platform will be a better fit.

Unfortunately I'm having to learn Obj-C at the same time. In the Java game, there were a few libraries that were instantiated by the main game class, and accessed statically when needed. This meant that I could the overhead of new instances each time.

i.e.:

Game.getRNG().nextInt() or Game.getNoiseGen().noise( x, y )

I'm trying to understand the best method to replicate this in Obj-C. I've looked at examples of singletons and am trying this method, but I'm not sure if this is the best way to do this.

The above code, would apparently translate into something like:

[[[Game getInstance] getNoiseGen] noise]

Is there a better way to create a single instance of library classes and statically reference them from anywhere inside my application code?

helion3
  • 34,737
  • 15
  • 57
  • 100
  • 2
    Just a side note about style: a more standard Objective-C translation would be `[[[Game sharedGame] noiseGenerator] noise]`. – mipadi Jun 17 '13 at 19:16

2 Answers2

0

For creating a singleton make sure you only create the static instance once, you can do this by using GCD.

+ (id)sharedInstance;
{
    static dispatch_once_t once;
    static id sharedInstance;
    dispatch_once(&once, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

and you can then:

Game *game = [Game sharedInstance];
//... 
CGPoint point = CGPointMake(x, y);
[[game noiseGen] noise:point]
//...

Singletons are considered an anti-pattern and should be avoided if possible.

Just from looking at your example you also violate the Law of Demeter, might be worth a read to see if you can improve there.

Oliver Atkinson
  • 7,970
  • 32
  • 43
  • The only alternative I can think of, is having a parent class create a single instance and passing that as an argument to the subclasses that use it. In my example, the random number generators and noise generators are needed by every chunk/tile to properly calculate their height values. It's too inefficient for them to create a new instance for every single tile rendered. – helion3 Jun 17 '13 at 19:34
  • Could you not create a ChunkDelegate which has methods to calculate their height? `- (CGFloat)heightForChunk:(Chunk *)chunk;` and pass the reference to self from the class which creates the chunk, and make it conform to the protocol? (protocols are similar to interfaces in java) – Oliver Atkinson Jun 17 '13 at 19:39
  • check here: http://pastebin.com/5E6uF1LG I created an sample solution, so you can see what I am talking about. – Oliver Atkinson Jun 17 '13 at 19:48
0

If I understand your Java model correctly there is no reason why you cannot translate it directly into Objective-C, which certainly has the advantage that you are familiar with it - helpful while you're learning a new language.

By "there were a few libraries that were instantiated by the main game class, and accessed statically when needed" I take it that either:

  1. The main class declared global variables of the appropriate types and initialised them; or
  2. The classes themselves have a global variable which holds a reference to an instance of themselves.

You are not generating "singletons" here, just objects you want to share. Also given that your main class created them they always exist. Combined this means you've no need to use any of the "singleton" schemes which delay creation until first use and deal with one-time thread-safe initialisation.

All you need is the model for global variables in Objective-C. In outline this is:

  1. Declare the variable as extern in a header file
  2. Define the variable in an implementation file

In outline, your MainGame.h:

#import "SharedGameObject.h"

@interface MainGame : NSObject

extern SharedGameObject *TheSharedGameObject;
...
@end

and MainGame.m

#import "MainGame.h"

SharedGameObject *TheSharedGameObject;

- (id) init
{
   TheSharedGameObject = [SharedGameObject new];
   ...
}

Now every other class which import MainGame.h has access to the same shared object using TheSharedGameObject. This is the model Apple used for NSApplication and it associated NSApp global variable.

HTH

CRD
  • 52,522
  • 5
  • 70
  • 86
  • 1
    This is wrong on several levels. Firstly, this isn't thread safe, so what happens if multiple threads all try to access this at the same time? There is no reason 1 thread couldn't be in the middle of allocating the object while the other one is trying to access the object This is actually what Apple shows in its documentation. http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html#//apple_ref/doc/uid/TP40002974-CH4-SW32 – Oliver Atkinson Jun 17 '13 at 19:50
  • I did mean that a main game class instantiated the libraries, and then had a public static method to return those for use elsewhere in the code. – helion3 Jun 17 '13 at 19:55
  • @BotskoNet - You can implement that pattern as well (as I said, there is nothing wrong with following your Java design in this case). If you drop the `extern...` from the header and replace it by an `+ (SharedGameObject *)theSharedGameObject;` with an implementation that just returns the value of `TheSharedGameObject`. – CRD Jun 17 '13 at 20:10
  • @OliverAtkinson - The OP is creating the objects at startup - an applications NSApplication subclass or application delegate is initialised *once* - there are 0 issues with thread safety. Sure, you can't do this in general, in an arbitrary class, which might be called from arbitrary threads, but that is not the case here. If you are concerned that the application delegate will be created too late there are ways around that without worrying about threading - but as the OP was using this pattern in their Java code I'm assuming it will be OK (as it usually is, how many `load`'s have you needed?) – CRD Jun 17 '13 at 20:16
  • 1
    @CRD I don't think its wise to advise placing static variables in the AppDelegate, code becomes difficult to maintain and impossible to re-use. It might be OK for Java, but don't think it really works for objC. – Oliver Atkinson Jun 17 '13 at 20:22
  • @OliverAtkinson - Sometimes pragmatism wins. You wouldn't construct a complex program this way, but then one shouldn't also eschew `NSApp` because its a global. This has nothing to do with Java vs. Obj-C, "might be OK for Java" indeed... – CRD Jun 17 '13 at 20:45