I am somewhat new to iOS 5 singletons and am using a singleton as documented here:
iOS 5 Singletons
Something like this:
MyManager.h
#import <Foundation/Foundation.h>
#interface MyManager : NSObject
//Data for section 1
#property(nonatomic,copy) NSString * section1a;
#property(nonatomic, assign) NSUInteger section1b;
//Data for section 2
#property(nonatomic,copy) NSString * section2a;
#property(nonatomic, assign) NSUInteger section2b;
+ (id)sharedInstance;
#end
MyManager.m
#implementation MyManager
#synthesize section1a, section1b, section2a; , section2b;
+ (id)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init]; // or some other init method
});
return _sharedObject;
}
#end
So I use it as follows:
MyManager * myManager = [MyManager sharedInstance];
myManager.data = self.data
Is this how you generally use the singleton? Am I missing anything?
Sorry for the basic questions I just want to make sure I am doing it right.
Thanks
You're doing it right. This is (currently :) the way to go and works fine with ARC enabled, multiple threads etc.
It isn't strictly a singleton, as you can alloc more instances of the class, but I don't think that's relevant in your case.
This is not a singleton because you can create multiple instances of this class with alloc/init methods.
A right answer is here
I usually make my singleton as such (suppose we are making a singleton for Manager):
static (Manager *) instance;
+ (Manager *) getInstance
{
if(!instance) instance = [[Manager alloc] init];
return instance;
}
This is a very basic singleton (it doesn't take into account multithreading or other advanced features) but this is the basic format.
Refer to this: Singleton in iOS 5? You should override allowWithZone to prevent rogue code from creating a new instance.
Related
I am trying to access an Objective C singleton from Swift, however I only seem to get the initial value created in the init function of the singleton. The flightControllerState object exposed is updated in a delegate function and I can see that the value is properly updated on the Objective C side.
I have followed a few different posts here on SO and also this article on how to call the shared object from Swift. (I should also mention this is running inside a react native project if that may have any impact?)
EDIT updated swift code - I added the wrong line to the init method to grab shared instance - issue is still the same
Objective-C Singleton
#import DJISDK;
#interface RCTBridgeDJIFlightController : RCTEventEmitter<DJIFlightControllerDelegate> {
DJIFlightControllerState *flightControllerState;
}
#property(nonatomic, readonly) DJIFlightControllerState *flightControllerState;
+ (id)sharedFlightController;
#end
#implementation RCTBridgeDJIFlightController
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
+ (id)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (id)init {
// I also tried this to make sure the shared instance was returned but no luck
//if (sharedFlightControllerInstance != nil) {
// return sharedFlightControllerInstance;
//}
if (self = [super init]) {
flightControllerState = nil;
}
return self;
}
-(void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
flightControllerState = state;
}
#end
Swift class calling singleton and accessing values
class VirtualStickController {
var flightControllerSharedInstance: RCTBridgeDJIFlightController
override init() {
self.flightControllerSharedInstance = RCTBridgeDJIFlightController.sharedFlightController()
}
func getFlightControllerState() {
if let state = flightControllerSharedInstance.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)") // always null
} else {
print ("NULL")
}
}
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
There is no need to use #synthesize for properties in (modern) Objective-C except in special circumstance.
The property flightControllerState is an instance property and will be synthesised (with or without the #synthesize) using a hidden instance variable for its storage.
The variable flightControllerState is a global variable, it happens to have the same name as the property but has no connection whatsoever with it.
At a guess you are changing the global variable in Objective-C and expecting to see the result in Swift via the property, you won't.
Remove the global variable and then check the rest of your code.
Apart from that your code produces a valid shared instance which can be shared between Objective-C and Swift and changes made in one language will be visible in the other.
HTH
Regarding the titular question about how to access an Objective C singleton from Swift, I would recommend an alternative. Modern convention is to declare your sharedFlightController as a class property and declare init as NS_UNAVAILABLE:
#interface RCTBridgeDJIFlightController : NSObject
...
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
The implementation would implement a getter for this class property:
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
...
#end
Now, your Swift code can reference RCTBridgeDJIFlightController.shared, as is the convention with Swift singletons.
Regarding why you are receiving a nil for the status, there are one of two possible problems:
You Objective-C code has confusing combination of explicitly defined ivars, manual synthesis, and global variables. (See below.)
I would also suggest that you confirm whether flightController:didUpdateState: is ever getting called at all. (I don't see you ever setting the delegate of the flight controller.) Add a breakpoint or NSLog statement in that method and confirm.
On the first issue, above, I would suggest:
You should not use those commented lines in your init method. If you want to make sure that your singleton object is used, then declare init as NS_UNAVAILABLE.
Given that all your init method is doing is updating flightControllerState to nil, you can remove it entirely. In ARC, properties are initialized to nil for you.
You should not declare explicit ivar in your #interface. Let the compiler synthesize this automatically for you.
You should not #synthesize the ivar in your #implementation. The compiler will now automatically synthesize for you (and will use an appropriate name for the ivar, adding an underscore to the property name.
You should not declare that global in your #implementation.
If you want to use this sharedFlightController from Swift, you should define it to be a class property, not a class method. I know that that article suggested using a class method, but that really is not best practice.
Thus:
// RCTBridgeDJIFlightController.h
#import <Foundation/Foundation.h>
// dji imports here
NS_ASSUME_NONNULL_BEGIN
#interface RCTBridgeDJIFlightController : NSObject
#property (nonatomic, readonly, nullable) DJIFlightControllerState *flightControllerState;
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
NS_ASSUME_NONNULL_END
And
// RCTBridgeDJIFlightController.m
#import "RCTBridgeDJIFlightController.h"
#interface RCTBridgeDJIFlightController ()
#property (nonatomic, nullable) DJIFlightControllerState *flightControllerState;
#end
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
NSLog(#"State updated");
self.flightControllerState = state;
}
#end
The end result is that you can now use it like so:
class VirtualStickController {
func getFlightControllerState() {
if let state = RCTBridgeDJIFlightController.shared.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)")
} else {
print("NULL")
}
}
}
Note, because the sharedFlightController is now a class property, Swift/ObjC interoperability is smart enough so the Swift code can just reference shared, as shown above.
I have an NSObject subclass like this:
#interface MyManager : NSObject
+ (MyManager *)sharedInstance;
// More instance methods
#end
#implementation MyManager
+ (MyManager *)sharedInstance
{
static dispatch_once_t _singletonPredicate;
static MyManager *sharedObject = nil;
dispatch_once(&_singletonPredicate, ^{
sharedObject = [[self alloc] init];
});
return sharedObject;
}
// More instance methods implemetation
#end
I call it throughout the app this way:
[[MyManager sharedInstance] anInstanceMethod];
without linking it to any property anywhere. Is this correct or appropriate, or should it be recommended to keep a property to this kind of objects anywhere?
On the other hand, I have my app enabled for keeping updating locations in background mode, and I need this object to keep listening for events from the location manager while the app is running in background. How should I handle this object in such scenario?
Thanks in advance
Singleton exists once it is created, and never destoryed.
I know there are several threads on this, but none answer my questions.
I've implemented my singleton class like this (being aware of the controversy about singletons):
+ (MyClass*) sharedInstance {
static MyClass *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[MyClass alloc] init];
});
return _sharedInstance;
}
- (instancetype)init{
self = [super init];
if (self) {
//setup code
}
return self;
}
I tried instantiating a different object and compared it to the one returned by sharedInstance with '==' and they were indeed different.
Questions:
Shouldn't creating more than one object of the singleton class be impossible? Isn't that the point? Singleton implementation in Java prevents it.
And if so, how? Should I make a setup method and call it instead of having the init implemented and doing it?
Is this correct implementation?
Your observation is correct, many of the "singleton" patterns you see in Objective-C are not singletons at all but rather a "shared instance" model where other instances can be created.
In the old MRC days Apple used to have sample code showing how to implement a true singleton.
The code you have is the recommended pattern for ARC and thread-safe singletons, you just need to place it in the init method:
- (instancetype) init
{
static MyClass *initedObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
initedObject = [super init];
});
return initedObject;
}
This code will ensure that there is only ever one instance of MyClass regardless of how many [MyClass new] or [[MyClass alloc] init] calls are made.
That is all you need to do, but you can go further. First if you wish to have a class method to return the singleton it is simply:
+ (instancetype) singletonInstance
{
return [self new];
}
This method ends up calling init which returns the singleton, creating it if needed.
If MyClass implements NSCopying then you also need to implement copyWithZone: - which is the method which copy calls. As you've a singleton this is really simple:
- (instancetype) copyWithZone:(NSZone *)zone
{
return self;
}
Finally in Objective-C the operations of allocating a new object instance and initialising it are distinct. The above scheme ensures only one instance of MyClass is initialised and used, however for every call to new or alloc another instance is allocated and then promptly discarded by init and cleaned up by ARC. This is somewhat wasteful!
This is easily addressed by implementing allocWithZone: (like copy above this is the method alloc actually ends up calling) following the same pattern as for init:
+ (instancetype) allocWithZone:(NSZone *)zone
{
static MyClass *allocatedObject;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
allocatedObject = [super allocWithZone:zone];
});
return allocatedObject;
}
The first time an instance is created then allocWithZone: will allocate it and then init will initialise it, all subsequent calls will return the already existing object. No discarded unneeded allocations.
That's it, a true singleton, and no harder than the faux-singletons that are so common.
HTH
You can't make the init method private, like you would do in Java with the constructor. So nothing stops you from calling [[MyClass alloc] init] which indeed creates a different object. As long as you don't do that, but stick to the sharedInstance method, your implementation is fine.
What you could do: have the init method raise an exception (e.g. with [self doesNotRecognizeSelector:#_cmd]) and perform the initialization in a different method (e.g. privateInit) which is not exposed in the header file.
With objective-c, you can prevent your singleton class to create more than one object. You can prevent alloc and init call with your singleton class.
#import <Foundation/Foundation.h>
#interface SingletonClass : NSObject
+ (id) sharedInstance;
- (void) someMethodCall;
- (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead")));
+ (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead")));
#end
#import "SingletonClass.h"
#implementation SingletonClass
+ (id) sharedInstance{
static SingletonClass * sharedObject = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedObject = [[self alloc] initPrivate];
});
return sharedObject;
}
- (instancetype)init {
#throw [NSException exceptionWithName:NSInternalInconsistencyException reason:[NSString stringWithFormat:#"You can't override the init call in class %#", NSStringFromClass([self class])] userInfo:nil];
}
- (instancetype)initPrivate {
if (self = [super init]) {
}
return self;
}
- (void) someMethodCall{
NSLog(#"Method Call");
}
#end
1# If you will try to call init or new methods on SingletonClass, then these methods would not be available to call.
2# If you comment out mentioned below methods in header file and try to call the init on SingletonClass method then app will be crashed with reason "You can't override the init call in class SingletonClass".
- (instancetype) init __attribute__((unavailable("Use +[SingletonClass sharedInstance] instead")));
+ (instancetype) new __attribute__ ((unavailable("Use +[SingletonClass sharedInstance] instead")));
Just use this code to create single object to Singleton Pattern and prevent alloc init call for singleton pattern from other classes. I had tested this code with xCode 7.0+ and its working fine.
To prevent creating multiple objects of single class, you need to do following things. you just fine to created singleton object. but while calling init, copy, mutable copy, you need to handle such way.
- (instancetype)init{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
- (id)copy{
if (!_sharedInstance) {
_sharedInstance = [MyClass sharedInstance];
}
return _sharedInstance;
}
the same things for mutable copy as well. so this implementation make sure that once one instance is available throughout..
May this help you.
#VeryPoliteNerd just mark the init and new methods as unavailable on the .h:
- (instancetype)init __attribute__((unavailable("Use +[MyClass sharedInstance] instead")));
+ (instancetype)new __attribute__((unavailable("Use +[MyClass sharedInstance] instead")));
This will cause the compiler to complain if a caller tries to manually instantiate this objects
This works for me:
static AudioRecordingGraph * __strong sharedInstance;
+(instancetype)sharedInstance {
#synchronized(self) {
if(!sharedInstance) {
sharedInstance = [AudioRecordingGraph new];
}
return sharedInstance;
}
}
I wanted to add observation for a modern implementation of Objective-C singletons for optimal Swift interoperability. But let me start with the code. Let’s assume for a second that the class was to manage a series of requests, so I might call it a RequestManager:
// RequestManager.h
NS_ASSUME_NONNULL_BEGIN
#interface RequestManager : NSObject
#property (nonatomic, strong, readonly, class) RequestManager *sharedManager;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)copy NS_UNAVAILABLE;
- (instancetype)mutableCopy NS_UNAVAILABLE;
- (void)someMethod;
#end
NS_ASSUME_NONNULL_END
And
// RequestManager.m
#implementation RequestManager
+ (instancetype)sharedManager {
static RequestManager *_sharedInstance = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
//setup code
}
return self;
}
- (void)someMethod { ... }
#end
Note:
Note, I made the singleton a class property rather than a class method.
From the Objective-C side, it is a distinction without difference, but from Swift, you can do things like MyClass.shared without the noise of the (). This is the pattern that Apple has adopted with all of their singletons.
One might give the singleton a more meaningful name. For example, if the class was a RequestManager, the singleton might be called sharedManager rather than sharedInstance.
If you do this, Swift will automatically detect the salient portion of the instance name and will expose it to Swift as shared, not sharedInstance or whatever.
If you really want to use the name sharedInstance in your Objective-C code, then will just need to supply an explicit NS_SWIFT_NAME of shared to adopt the nice, concise, and consistent singleton naming practice in Swift:
#property (nonatomic, strong, readonly, class) RequestManager *sharedInstance NS_SWIFT_NAME(shared);
Note the use of NS_UNAVAILABLE. This prevents callers from accidentally instantiating their own copies. E.g. from Objective-C:
Or from Swift:
Probably needless to say, I’ve audited this for nullability, namely using the NS_ASSUME_NONNULL_BEGIN/END to make the default non-null. Or, obviously, you could just mark the singleton property as nonnull.
Calling alloc / init to get a second instance of a Singleton class is considered a blatant programming error. To avoid this kind of programming error, you don't write complicated code to prevent it, you do code reviews and tell off everyone trying to do it, as you would with any programming error.
This works for me :
static DataModel *singleInstance;
+ (DataModel*)getInstance{
if (singleInstance == nil) {
singleInstance = [[super alloc] init];
}
return singleInstance;
}
You can call it with
_model = [DataModel getInstance];
I'm working in Xcode using Spritekit and I'm struggling to build a separate class to display high scores.
Level 1 has a property called highscore(#synthesize highscor = highscore;) as its name suggests, records the highest scores, but I can't figure out how to send it to the high score class.
JKGLevel1 *class1Instance = [[JKGLevel1 alloc] init];
int x = class1Instance.highscor;
That's how I tried it, but when I leave level 1 and go to the high score class, it keeps telling me that the high score is 0.
First of all you do not need to #sythesize anything anymore, but thats just an aside. What you will need to do is create a singleton class, i.e.: a class with properties that are only instantiated once and can be accessed ANYWHERE in you code where you please.
For example I use this for storing account information :
In the .m file
+ (id)sharedManager {
static AppManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
return [super init];
}
in the .h file
#import <Foundation/Foundation.h>
#import <Accounts/Accounts.h>
#interface AppManager : NSObject
#property (nonatomic, retain) ACAccount *twitterAccount;
#property (nonatomic, retain) NSMutableArray *arrayOfAccounts;
+ (id)sharedManager;
#end
**Then in any file I want to use this : **
AppManager *sharedManager;
sharedManager = [AppManager sharedManager];
NSString *usrName = sharedManager.twitterAccount.username;
Hope this actually helps you. There are also other ways you can do this by just creating a class that you manage carefully without this singleton nonsense, but this is a cleaner way of doing it.
Do something like this:
In your HighScoreClass make a property named lavel1HighScore. (if you don't want to make property for each level then you can do it by an array. :) )
When your level 1 is complete pass the highScore of level1 to that property of HighScoreClass.
Like:
JKGLevel1 *class1Instance = [[JKGLevel1 alloc] init];
HighScoreClass *highScore = [[HighScoreClass alloc] init];
highScore.lavel1HighScore = class1Instance.highscor;
Hope this helps.. :)
You could put the scores in a plist to be read in viewdidload or if there is a reason to pass a live score object from level to level -
I would suggest that you put in your scores in a separate class (just an ordinary class with a header and method (.h & .m) and just make the class available to all your levels (class) this can easily be done via the following code:
... levelx.h
#import <UIKit/UIKit.h>
#class Class_scores;
#interface LevelxViewController : UIViewController <> {
}
#property (nonatomic, strong) Class_scores *highscore_class;
needless to say you will need to define a function in the (high_score) class to return the high score and call it from your level class.
I was wondering how can I share a NSDictionary objects among several view controllers which basically are tabs in my application.
I tried using a protocol, like in Java, so that I can cast to the protocol and access the property. That doesn't seem to work.
Also I had a look at similar question at
How to share data globally among multiple view controllers
But I observed that the appDelegate method is not safe and may lead to memory leak.
Similarly injection on class A into class B will create the same problem.
So can anyone suggest me any method which i should study or implement in my code?
If you wants to share only Dictionary, why don't you go for class method using from helper class.
+(NSDictionary *)shareMethod
{
return dict;
}
You can use singleton class for sharing the data
Check this Singleton class
MyManger.h
#import <foundation/Foundation.h>
#interface MyManager : NSObject {
NSMutableDictionary *_dict
}
#property (nonatomic, retain) NSMutableDictionary *dict;
+ (id)sharedManager;
#end
MyManger.m
#import "MyManager.h"
static MyManager *sharedMyManager = nil;
#implementation MyManager
#synthesize dict = _dict;
#pragma mark Singleton Methods
+ (id)sharedManager {
#synchronized(self) {
if(sharedMyManager == nil)
sharedMyManager = [[super allocWithZone:NULL] init];
}
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
dict = [[NSMutableDictionary alloc] init];
}
return self;
}
You can access your dictionary from everywhere like this
[MyManager sharedManager].dict
I found a way out. Since I want a dictionary to be shared across, I declared a method in my protocol
- (void) setSingleHouse:(NSDictionary*) singleHouse;
In each of my controller I implemented the method in a appropriate manner. Hence I was able to share across them for now.
Also I figured out that I was casting in a wrong way earlier i.e (#protocol(protocol name)). Now changed it into NSObject .
Sorry for the fuss.
I advise you not to use a property in your appDelegate instance. This pattern doesn't improve code reusability.
Basicly your shared NSDictionnary is your model. So, if you want your code to follow the MVC design parttern, you should create a class with a shared instance that would contain your NDDictionnary in a public property.
This is well explained in this post
This way, you will access your shared NSDictionnary this way
:
NSDictionary* sharedDictionnary = [MyModel sharedInstance].sharedDictionary;