Accessing a class variable from different a class - ios

A very common, generic question but it seems that everybody has it's own opinion about accessing variables that belong in another class. What I want is to use boolean variable in class2 to perform a statement. Example of what I want is:
Class1.h
#Interface Class1{
bool boolean;
}
#Property (nonatomic, retain) bool boolean;
Class1.m
#synthesize boolean;
Class2.m
if(class1.boolean == YES){
Do Something
}
The if statement is class2 doesn't seem to work, as I tried to print the boolean value in class2 and all it returns is false. I want to get the current value of class1 boolean variable and use it in class 2 without initialising it.

Looking at your question, it seems you want to create an instance of 'Class1' in another class, get the properties value to be presented there.
In that case, whenever you instantiate 'Class1', it comes with the initial values. That means the values will be 'null' for sure. If you want to get the changed value, you need to create 'Class1' as Singleton class, where, this class will be instantiated once in the whole application. Means change the value of 'boolean1' in any class, and get the same value in another, whenever or wherever you want.
But again, it totally depends on how you want to use the whole thing.
Singleton example:
// Class1.h
#interface Class1 : NSObject
/**
* A boolean property
*/
#property (nonatomic, strong) BOOL *boolean;
// Class1.m
#implementation Class1
// This is actual initialisation of the class instance.
+ (Class1 *)sharedInstance {
static Class1 *sharedInstance = nil; //static property, so that it can hold the changed value
// Check if the class instance is nil.
if (!sharedInstance) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// If nil, create the instance, using dispatch_once, so that this instance never be created again. So, if needed, app will use the existing instance.
sharedInstance = [[super allocWithZone:NULL] init];
// custom initialisation if needed
});
}
return sharedInstance;
}
// So that, if somebody uses alloc and init, a new instance not created always.
// Rather use existing instance
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
// So that, if somebody uses alloc and init, a new instance not created always.
// Rather use existing instance
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#end
Now updating and using the value.
//Class2.m
#import "Class1.h"
Class1 *myinstance = [[Class1 alloc] init];
myinstance.boolean = YES;
Getting the value on another class
//Class3.m
#import "Class1.h"
Class1 *myinstance = [[Class1 alloc] init];
if(myinstance.boolean == YES){
Do Something
}

Related

Share singleton from Objective C to Swift

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.

Access to variable value from another class in Objective C

ClassA.h
...
#property (weak, nonatomic) NSString *myVariable;
- (id) setMyVariable:(NSString *)string;
- (id) getMyVariable;
ClassA.m
...
#synthezise myVariable = _myVariable;
... some inits
- (id) setMyVariable:(NSString *)string {
_myVariable = string;
NSLog(#"here nslog success return new value: ", _myVariable);
return _myVariable;
}
- (id) getMyVariable {
NSLog(#"here nslog return nil", _myVariable);
return _myVariable;
}
ClassB.m
#import ClassA.h
...
ClassA *classA = [[ClassA alloc] init];
[classA setMyVariable:#"some"];
ClassC.m
#import ClassA.h
...
ClassA *classA = [[ClassA alloc] init];
NSLog(#"here nslog returns nil: #%", [classA getMyVariable]);
Why does [ClassC getMyVariable] return nil? Same result when I try to set value directly without setter and getter. I already read other topics on StackOverflow and Google, but have not idea why it doesn't work.
Your whole code is a bit of a mess really. Why are you using a weak property? Why are you using a #synthezise since this is is automatically done by xcode for you along with the getters and setters so you don't need to create them ever.
The reason why your [classA getMyVariable]; is nil in ClassC is because you create a new instance of it on the line above. By the looks of what you are trying to do is you want to set the variable for instance of a class in one class and access that variable on the same instance in a different class. So one method of doing this is to use a singleton, these are sometimes not liked but I think they work well and don't see a reason why some (not all) developers don't like them.
So lets do some cleaning up and try implementing a singleton
ClassA.h
#interface ClassA : NSObject
#property (nonatomic, strong) NSString *myVariable;
// No need for you to create any getters or setters.
// This is the method we will call to get the shared instance of the class.
+ (id)sharedInstance;
#end
ClassA.m
#import "ClassA.h"
#implementation ClassA
// No need to add a #synthezise as this is automatically done by xcode for you.
+ (id)sharedInstance
{
static ClassA *sharedClassA = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// If there isn't already an instance created then alloc init one.
sharedClassA = [[self alloc] init];
});
// Return the sharedInstance of our class.
return sharedClassA;
}
#end
Right so we have cleaned our ClassA code up and added a method for getting a shared instance of ClassA so now to ClassB
ClassB.m
// Other code in ClassB
// Get the shared instance
ClassA *classA = [ClassA sharedInstance];
// Set the value to the property on our instance.
[classA setMyVariable:#"Some String Value"];
//........
Now that ClassB has set the variable we can go to ClassC now and look at it.
// Other code in ClassC
// We still need to have an instance of classA but we are getting the sharedInstance
// and not creating a new one.
ClassA *classA = [ClassA sharedInstance];
NSLog(#"My variable on my shared instance = %#", [classA myVariable]);
//........
Might help if you read this and this for help on understanding different design patterns
because you don't set a value after creating an object. i should be like this:
ClassA *classA = [ClassA alloc] init];
[classA setMyVariable:#"some"];
NSLog(#"not nil anymore: #%", [classA getMyVariable]);
BTW: the #property tag provides two keywords to set getter and setter methods.
#property (weak, nonatomic, getter=myVariable, setter=setMyVariable:) NSString *myVariable;
and apple avoids the word "get" in getter-methods...

Saving and accessing a variable from a separate class

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.

Using singleton to create an array and declare it in TableView

I am trying to create a singleton NSMutableArray so I can use it on TableView as Titles. How to declare it in a TableView?
e.g.
cell.textLabel.text = ??? //Don't know how to declare it from the singleton.
Here is my singleton.m:
#import "Singleton.h"
#implementation Singleton
#synthesize Trees;
+(Singleton *) theTrees {
static dispatch_once_t pred;
static Singleton *theTrees = nil;
dispatch_once(&pred, ^ {
theTrees = [[Singleton alloc] init];
theTrees.Trees = [[NSMutableArray alloc] init];
});
return theTrees;
}
-(id) init {
self = [super init];
if(self) {
// Variables
[Trees addObject:#"Berzas"];
[Trees addObject:#"Liepa"];
[Trees addObject:#"Drebule"];
}
return self;
}
#end
Please point me in the right direction, thank you.
It seems there is no need to create a singleton class here. Just have a class or instance method somewhere
- (NSDictionary *)trees
and the implementation about the same as your method that returns a singleton, but just creating a singleton NSDictionary once.
Properties like "Trees" should be written in lowercase: "trees", not "Trees".
Instance variables should start with an underscore, so
[_trees addObject:#"Berzas"];
That's done so that you always can distinguish between using accessor methods and accessing instance variables; they are not the same thing. Usually you would only access instance variables in your init method, and when implementing getters and setters for properties.

Variable in Objective-C Singleton getting reset

I created an Objective-C Singleton class. I have a few properties on the class, including a BOOL property. For some strange reason the BOOL variable that I declared "resets" itself to YES outside of the scope that is set in.
In the Singleton class's header, the BOOL is declared using #property with the following parameters:
#property (nonatomic, assign, readonly) BOOL shouldCryptData;
In the Singleton class's #interface in the implementation, I redefine the same property as readwrite (because I need it to be read only to outside classes, but read / write to my own).
#interface SingletonClassName ()
#property (nonatomic, assign, readwrite) BOOL shouldCryptData;
#end
The BOOL property gets set during initialization of the singleton class. I am setting it like this inside one of the init methods. There are multiple init methods which specify whether or not data should be crypted or not - this is just one example of where it would be set. Only one of my init methods call super, all the others make a call to the main init.
- (id)init {
self = [super init];
if (self) {
// Brief setup code
[self setShouldCryptData:NO]; // Have also tried using dot-notation and without *self*
// I can confirm that the shouldCryptData property is NO (within the current scope) right after setting it in this method
}
return self;
}
Now, the odd part is that when I try to access shouldCryptData from any other method, it always returns YES. Why would it return YES after explicitly setting it to NO?
I'm not accessing it in any strange way, just like this:
if (self.shouldCryptData == YES) // Outside of the init method, this is ALWAYS true
I know I'm doing something wrong, but I can't figure it out. I feel like it has something to so with the singleton, but I'm not sure. It seems like neither Google, nor StackOverflow have any answers for this. Any ideas?
EDIT
Singleton implementation:
//-------- Header ---------------------//
#interface SingletonClassName : NSObject
+ (SingletonClassName *)sharedManager;
#end
//-------- Implementation ------------//
#implementation
+ (SingletonClassName *)sharedManager {
static SingletonClassName *sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (sharedManager == nil) sharedManager = [[super allocWithZone:NULL] init];
});
return sharedManager;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedManager];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
The simplest explanation is that you don't quite have a singleton, and you're setting that property on one instance, and reading it on another.
Try doing NSLog(#"%p", self) in all the methods where you set and access the property and make sure they're all the same.
Added:
This is my usual singleton logic:
+ (instancetype) sharedInstance {
static MyClass *singleton;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[self alloc] init];
});
return singleton;
}
Invoke [MyClass sharedInstance] when you need it.
Figured out what was happening using a combination of suggestions from the answers and comments here. The best suggestion, by #Paul.s, was to place a breakpoint on the property declaration:
Are you sure that nothing else is setting it to YES? Place a breakpoint on the property declaration and it should break every time the property is read or written to
#Andy's comment was also helpful in figuring out what the issue was:
You could also try setting a watch point on the variable, which will pause anytime the variable is changed. You can do this firstly setting a breakpoint in the init method of the singleton, and then by typing watchpoint set variable shouldCryptData in the debugger once you reach the breakpoint.
I found that the init method was getting called multiple times, and each time with a different setting for the BOOL property, shouldCryptData.
Another suggestion from #noa helped clear up which instance the BOOL property was getting set on. I originally set it from the shared manager, but later set it in the init method because the sharedManager was on a different instance than the rest of the methods. Here's the relevant excerpt from the answer:
Try doing NSLog(#"%p", self) in all the methods where you set and access the property and make sure they're all the same.
So, it turns out that there were multiple issues:
The property was being set multiple times (with multiple unneeded calls to init)
Turns out that I was originally setting the property on a different instance
My singleton wasn't being correctly created (although that didn't really affect the property)
KVO is one of ways. I will give an example.
TestObject.h
#interface TestObject : NSObject
#property (nonatomic, assign) BOOL isIt ;
#end
TestObject.m
#implementation TestObject
- (id)init
{
self = [super init] ;
if (self) {
[self addObserver:self forKeyPath:#"isIt" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil] ;
}
return self ;
}
- (void)dealloc
{
[self removeObserver:self forKeyPath:#"isIt"] ;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:#"isIt"]) {
NSLog(#"%#", change) ; // add a breakpoint here, you will know where change the value
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context] ;
}
}
#end
You will get notification only if the property is set by dot-notation or setIsIt method.

Resources