I want to initialize my singleton object which stores and manages the application settings over the entire class within my app. Also, the singleton instance should be initialized by loading the data from NSUserDefaults upon launch. However, I'm not fully sure where I should initialize the singleton upon launch.
In Cocoa app, I first wrote the singleton initialization code within applicationWillFinishLaunching:, taking parameters from NSUserDefaults. However, later I found that this doesn't work properly if I also write the singleton initialization code (taking no parameter!) within my initial view controller, set in storyboard, because the viewWillLoad:, viewDidLoad: etc. of the class of the view controller are called before the applicationWillFinishLaunching:.
So now I'm sure I should write the singleton initalization code within viewWillLoad: earlier than applicationWillFinishLaunching, but still not sure whether it is appropriate. Specifically, I know the NSApplicationMain is the first method to be called upon launch, but it seems that the next method is not anything within AppDelegate, at least if you use storyboard.
To summary, what I want to ask are the following:
What method from what class will be called after NSApplicationMain, if you use storyboard.
Where should I write my singleton initialization code within my app? I want to initialize it as soon as possible.
Does it differ between iOS and OS X app?
You should initialize it when it's first accessed. Something like this, maybe:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
As a side note, if you're literally only using this class as an accessor to NSUserDefaults, you might want to consider using static methods instead.
+ (id)mySpecificDataPoint {
return [[NSUserDefaults standardUserDefaults] objectForKey:#"whatever"];
}
+ (void)setMySpecificDataPoint:(id)data {
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"whatever"];
}
Or maybe a more well-designed way might be to add a category to NSUserDefaults for this purpose.
#interface NSUserDefaults (MyData)
#property (nonatomic) NSString *someDataPoint;
#property (nonatomic) NSInteger somePrimitiveDataPoint;
#end
#implementation NSUserDefaults (MyData)
- (NSString *)someDataPoint {
return [self objectForKey:#"someDataPoint"];
}
- (void)setSomeDataPoint:(NSString *)someDataPoint {
[self setObject:someDataPoint forKey:#"someDataPoint"];
}
- (NSInteger)somePrimitiveDataPoint {
return [[self objectForKey:#"somePrimitiveDataPoint"] integerValue];
}
- (void)setSomePrimitiveDataPoint:(NSInteger)somePrimitiveDataPoint {
[self setObject:#(somePrimitiveDataPoint) forKey:#"somePrimitiveDataPoint"];
}
#end
You init the singleton when you have to use it. So as Daji Djan said: lazy wins. Just take attention that, you should not do a long-run process in your applicationWillFinishLaunching, it should return as soon as possible.
If the singleton is not mandatory during applicationWillFinishLaunching, you should call it in viewWillAppear of first view controller if you need to initialize it ASAP.
lazy always wins
if you can get away with it: as late as possible :) AND always do the minimum needed (but do as much as is reasonable to keep your code clean!)
Related
I have some bluetooth class that needs to be alive during all views .
That means, that i have to call functions in this class from different views, and ALSO get delegates from this class to different views .
So , i have to initialise it once to create connection, than later, i have to use it again from different views(classes) and register to get notifications from it.
I was thinking about a singleton, that HE will create an instance of that bluetooth class, and i can access him from anywhere .
But, i would also like that any view can get delegates from it .
How would i do that ?
i have read What should my Objective-C singleton look like?
But maybe singleton is not what i need ?
How can you create a class to always be alive, and register to get delegates from it from anywhere ?
(how can it be done with app delegate class ? )
I have had the similar query a while back
Problem : Multiple Classes need to receive delegate calls from single instance
Solution: I used a combination of sharedInstance , delegates and NSNotifications to handle the problem
SharedInstance
+ (SomeBluetoothClass *) sharedInstance {
static dispatch_once_t once;
static SomeBluetoothClass *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Delegate
#property (weak, nonatomic) id <SomeBluetoothClassDelegate> delegate;
Since delegate can respond to only one class at a time. Assign class in focus based on your logic. Then whenever you want send info to all send it across using NSNotificationCenter from the sharedInstance. Send the info through using userInfo dictionary of NSNotifications
Notifications
[[NSNotificationCenter defaultCenter] postNotificationName:SomeBluetoothClassNotification
object:self
userInfo:info];
Model the structure of SomeBluetoothClass to be thread safe and handle all notifications along with the delegates and it should work fine.
Have a lots way for your situation. Firstly, you should understand that creating object is not too heavy.
So if you want to use Delegate, you can create a Factory method
Ex:
+ (instancetype)bluetoothManagerWithDelegate:(id<delegate>)delegate {
return [self alloc] initWithDelegate:delegate];
}
So that you also don't care about conflict of concurrence. Because you have separate Bluetooth class.
If you still want to use Singleton, in this situation, it depends on how many object you want to notify.
Only 1 views, just use delegate, and set new delegate when you present new view.
More than one, you can use NSNotificationCenter or Observer, you can google these keywords, it have a lot of tutorial and document on the internet help you use it.
I think you can create NSMutableArray in your singleton with links on your views and call someMethod for all objects when is needed. Don't forget remove views from array when its don't need anymore. That is simple realization of pattern called "Observer".
You need to create a class that should be allocated memory once in a lifetime
I am posting a small code snippet which can help you.
In your .m file
#pragma mark - Shared Instance
static BlootoothClass *_sharedblootoothclass = nil;
+ (BlootoothClass *) sharedClass {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedblootoothclass = [[self alloc] init];
// DO YOUR STUFF TO INTIALISE YOUR CLASS
});
return _sharedblootoothclass;
}
in Your .h file
+ (BlootoothClass *) sharedClass
dispatch_once is the queue with dispatch the instance of your class one in a lifetime and you can access its function all over in the app any time.
Now to get any data from it you can get it from instance from any where like
[BlootoothClass sharedClass].anyObject
And you also can send post notification from here in any of its function
- (void)detectedBlootoothdevice{
[[NSNotificationCenter defaultCenter] postNotificationName:#"newdevicedetected" object:nil];
}
You should not use delegates as you cant call same delegate function at multiple class because last delegate get overwritten.
Okay, so I was reading here declaring global variables in iPhone project and I noticed the line with this code: [[[UIApplication sharedApplication] delegate] myNSString];.
Basically, I want a user to input something in a text field on the flipside view, then have it stored in a variable which is accessed on the main view. Ideally, the main view would be able to just read the text field from the flipside view, but this seems to be impossible (I've spent several hours each day for the past few days scouring the web and various books for an answer about how to do this and no one seems to be able to give a definitive answer). Therefore, I'm resorting to using a global variable to tackle this.
Will the code that I printed above somehow allow me to do this? I've been trying to adapt it for the past hour, but have come up with nothing except No known instance method for selector 'myNSString' and I'm not quite sure what that means in this case.
Can someone please help me out? I feel like I can keep trying different things but without some sort of help, I'm just shooting in the dark here. Thank you!
You may want to think about using a singleton to hold your data if you're set on using a global variable. There's a good tutorial on singletons here: http://www.galloway.me.uk/tutorials/singleton-classes/ -basically it's a class that can be shared throughout the application and accessed/modified by different controllers. You'd be able to create a property on it, write to that property from the flip view, and then access that property from your main view.
#import "Singleton.h"
#implementation Singleton
#synthesize yourTextField;
#pragma mark Singleton Methods
+ (id)sharedManager {
static Singleton *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
yourTextField = #"";
}
return self;
}
You could call it in code by importing its header file and:
Singleton *mySingleton = [Singleton sharedManager];
the mySingleton object will have the text field attached. It can be accessed by:
mySingleton.yourTextField;
.h file:
#import <Foundation/Foundation.h>
#interface Singleton : NSObject
#property (nonatomic, strong) NSString *yourTextField;
+ (id)sharedManager;
#end
Singleton (remember about dispatch_once), static variables or NSUserDefaults. It really depends what you really need.
If you are using storyboards and just want to pass data between VC, then you can use "prepareForSegue" method (described here https://developer.apple.com/library/ios/documentation/uikit/reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instm/UIViewController/prepareForSegue:sender:).
Segue has "destinationController" property, so you can setup VC before showing it.
Class name MyData has 75+ properties that are needed in throughout 7 Scenes.
Currently, I pass the the instance of MyData file with the code below:
in SceneOne:
MyData *myData = [[MyData alloc]init];
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
SceneTwo *sceneTwo = [destinationViewController isKindOfClass:[SceneTwo class]] ? (SceneTwo*)destinationViewController : nil;
sceneTwo.myData = self.myData;
}
This allows me to access any properties as myData.anyProperty
When the 7th Scene is dismissed, I set myData = NULL, and the app returns to SceneOne and a new instance of MyData is initialized.
I'm trying to accomplish above via sharedInstance.
MyData.h
#import <foundation/Foundation.h>
#interface MyData : NSObject {
#property (nonatomic, retain) NSString *someProperty;
// 74 other properties
+ (id)sharedData;
#end
MyData.m
#import "MyData.h"
#implementation MyData
#synthesize someProperty;
+ (id)sharedData {
static Mydata *sharedData = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedData = [[self alloc] init];
});
return sharedData;
}
#end
Question 1: Will the code above work to access the same instance with the code below in any of the Scenes:
MyData *myData = [MyData sharedData];
Question 2: Can I still access the files as myData.anyProperty ?
Question 3: How do I trigger a new instance of MyData and delete the current instance?
Question 4: I'm downloading a myData file off the web that's a duplicate of MyData class but the properties contain data, and I want Singelton to provide a new instance with the data from the downloaded file, what would be the code for that? i.e myData = [newDownloadedFile copy];
Question 5: Is there an advantage of using Singleton Method Vs. my current method?
Yes
Yes
You don't. Why do you believe this is necessary? Can you instead add a reset method to MyData?
You shouldn't mix the singleton pattern with a multiple-instance usage case. If you truly want a singleton, think about adding an additional layer to your data set. For example, you may have a local data and remote data configuration. If this is what you want, you may have to change the interface (header) of MyData to make this possible.
A singleton is a single instance of a class across a process. When you want to access the same collection of data from multiple locations in your code, a singleton object is one way you can accomplish this. Otherwise you need to instantiate an object and pass its address to all interested classes so they are each accessing the same instance. That's an oversimplification but I believe it addresses your question.
Regarding your comment for number 3, if you have a singleton, you don't want to reset the data for the entire app if you simply don't need the data in one place anymore. So consider the impact that would have. If you no longer need the MyData object, simply don't use it anymore. Singleton objects typically persist for the lifetime of an app, so it is not common to release/dispose of the object.
For number 4, say you currently have a property called player with a method declaration like this:
- (Player *)currentPlayer;
If you have multiple configurations available, you would add a parameter to your method interface and implementation like this:
- (Player *)currentPlayerForConfiguration:(NSInteger)configuration;
You could decide to use a number, string, or something else to differentiate between different configurations of your data. If you use a number, 0 could be local, 1 could be remote, 2 could be either (for example, check local data first, and if not there, then check remote). If you only have two options, you could use a BOOL and define your method like this:
- (Player *)currentPlayerUsingLocalData:(BOOL)useLocalData;
I have a tab bar application that works fine, but if the app is opened without it having been running in the background, then the tabs are a little slower to open than usual, as they are loading plists.
Is it possible to load all the data into the views when the app launches?
I would recommend using a service class from which all of the view controllers can query. A common method of defining such a helper class is using the singleton design pattern. The singleton pattern allows only one instance of a singleton class to ever be instantiated. Using this method, you know that every other instance that will be making use of this service will be going through this one instance.
Here's the snippet I noramlly use, it might not be the optimal so I invite other users to suggest changes please:
//.h:
+ (MySingletonServiceInstance *)sharedInstance;
//.m:
static MySingletonServiceInstance *sharedInstance = nil;
+ (MySingletonServiceInstance *)sharedInstance{
#synchronized(self){
if(sharedInstance == nil)
sharedInstance = [[self alloc] init];
}
return sharedInstance;
}
- (id)init {
if ((self = [super init])) {
//Set up
}
return self;
}
Now in any other class (such as a view controller that needs some data), you can do something like this:
[[MySingletonServiceInstance sharedInstance] doSomething];
or
NSDictionary *myData = [MySingletonServiceInstance sharedInstance].data;
and it will call the same object. I often use singleton objects for loading data and suchlike, whether it's an interface to a web service or to the local CoreData. It's a really useful design pattern and I learned so much by picking it up.
I have my main app delegate
I have a few UIViewController derived instances driven by a Storyboard
Say I'd like to provide a centralized persistence layer for my application - perhaps Core Data of SQLite. Where would I put those objects? I'm missing some centrally accessible "Application" class you can access from all the UIViewController instances.
Is there a pattern to follow here?
you should check the singleton pattern:
In software engineering, the singleton pattern is a design pattern
that restricts the instantiation of a class to one object. This is
useful when exactly one object is needed to coordinate actions across
the system. The concept is sometimes generalized to systems that
operate more efficiently when only one object exists, or that restrict
the instantiation to a certain number of objects. The term comes from
the mathematical concept of a singleton.
here is a source for a example implementation: What should my Objective-C singleton look like?
and here is the direct link for the modern solution:
https://stackoverflow.com/a/145395/644629
What you're describing is your model layer. There are two main ways to manage the model:
At application startup, create the main model object and hand it to the first view controller.
Make the main model object a Singleton.
The "main model object" in both cases is generally some kind of object manager. It could be a document, or it could be a PersonManager if you have a bunch of Person objects. This object will vend model objects from your persistence store (generally Core Data).
The advantage of a Singleton here is that it's a little easier to implement and you don't have to pass around the manager. The advantage of a non-Singleton is that it's easier to have more than one (for a document-based system), and it's easier to test and reason about non-singletons than singletons. That said, probably 80% of my projects use a singleton model manager.
As a side note, that you appear to already understand: never store the model in the application delegate, and never use the application delegate as a "rendezvous point" to get to the model. That is, never have a sharedModel method on the application delegate. If you find yourself calling [[UIApplication sharedApplication] delegate] anywhere in your code, you're almost always doing something wrong. Hanging data on the application delegate makes code reuse extremely difficult.
Go with a singleton pattern, which has scope of application lifetime.
#interface DataManager ()
#end
#pragma mark -
#implementation DataManager
#pragma mark - Shared Instance
static DataManager* sharedInstance = nil;
#pragma mark - Singleton Methods
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (DataManager*)sharedInstance
{
#synchronized([DataManager class])
{
if (!sharedInstance) {
//[[self alloc] init];
sharedInstance = [[DataManager alloc] init];
}
return sharedInstance;
}
return nil;
}
+ (id)alloc
{
#synchronized([DataManager class])
{
NSAssert(sharedInstance == nil, #"Attempted to allocate a second instance \
of a singleton.");
sharedInstance = [super alloc];
return sharedInstance;
}
return nil;
}
#end
Declare your properties in .h file and synthesize them here in .m file.
To use that property just call:
// set value
[[DataManager sharedInstance] setSharedProperty:#"ABC"]; // If its a string
// get Value
NSLog(#"value : %#", [[DataManager sharedInstance] sharedProperty]);
Hope this is what you required.
Enjoy Coding :)