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...
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.
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
}
Is there a standard pattern for implementing a mutable/immutable object class pair in Objective-C?
I currently have something like the following, which I wrote based off this link
Immutable Class:
#interface MyObject : NSObject <NSMutableCopying> {
NSString *_value;
}
#property (nonatomic, readonly, strong) NSString *value;
- (instancetype)initWithValue:(NSString *)value;
#end
#implementation MyObject
#synthesize value = _value;
- (instancetype)initWithValue:(NSString *)value {
self = [self init];
if (self) {
_value = value;
}
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
Mutable Class:
#interface MyMutableObject : MyObject
#property (nonatomic, readwrite, strong) NSString *value;
#end
#implementation MyMutableObject
#dynamic value;
- (void)setValue:(NSString *)value {
_value = value;
}
#end
This works, but it exposes the iVar. Is there a better implementation that remedies this situation?
Your solution follows a very good pattern: the mutable class does not duplicate anything from its base, and exposes an additional functionality without storing any additional state.
This works, but it exposes the iVar.
Due to the fact that instance variables are #protected by default, the exposed _value is visible only to the classes inheriting MyObject. This is a good tradeoff, because it helps you avoid data duplication without publicly exposing the data member used for storing the state of the object.
Is there a better implementation that remedies this situation?
Declare the value property in a class extension. An extension is like a category without a name, but must be part of the class implementation. In your MyMutableObject.m file, do this:
#interface MyMutableObject ()
#property(nonatomic, readwrite, strong) value
#end
Now you've declared your property, but it's only visible inside your implementation.
The answer from dasblinkenlight is correct. The pattern provided in the question is fine. I provide an alternative that differs in two ways. First, at the expense of an unused iVar in the mutable class, the property is atomic. Second, as with many foundation classes, a copy of an immutable instance simply returns self.
MyObject.h:
#interface MyObject : NSObject <NSCopying, NSMutableCopying>
#property (atomic, readonly, copy) NSString *value;
- (instancetype)initWithValue:(NSString *)value NS_DESIGNATED_INITIALIZER;
#end
MyObject.m
#import "MyObject.h"
#import "MyMutableObject.h"
#implementation MyObject
- (instancetype)init {
return [self initWithValue:nil];
}
- (instancetype)initWithValue:(NSString *)value {
self = [super init];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
// Do not use the iVar here or anywhere else.
// This pattern requires always using self.value instead of _value (except in the initializer).
return [[MyMutableObject allocWithZone:zone] initWithValue:self.value];
}
#end
MyMutableObject.h:
#import "MyObject.h"
#interface MyMutableObject : MyObject
#property (atomic, copy) NSString *value;
#end
MyMutableObject.m:
#import "MyMutableObject.h"
#implementation MyMutableObject
#synthesize value = _value; // This is not the same iVar as in the superclass.
- (instancetype)initWithValue:(NSString *)value {
// Pass nil in order to not use the iVar in the parent.
// This is reasonably safe because this method has been declared with NS_DESIGNATED_INITIALIZER.
self = [super initWithValue:nil];
if (self) {
_value = [value copy];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
// The mutable class really does need to copy, unlike super.
return [[MyObject allocWithZone:zone] initWithValue:self.value];
}
#end
A fragment of test code:
NSMutableString *string = [NSMutableString stringWithString:#"one"];
MyObject *object = [[MyObject alloc] initWithValue:string];
[string appendString:#" two"];
NSLog(#"object: %#", object.value);
MyObject *other = [object copy];
NSAssert(object == other, #"These should be identical.");
MyMutableObject *mutable1 = [object mutableCopy];
mutable1.value = string;
[string appendString:#" three"];
NSLog(#"object: %#", object.value);
NSLog(#"mutable: %#", mutable1.value);
Some debugging right after the last line above:
2017-12-15 21:51:20.800641-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801423-0500 MyApp[6855:2709614] object: one
2017-12-15 21:51:20.801515-0500 MyApp[6855:2709614] mutable: one two
(lldb) po mutable1->_value
one two
(lldb) po ((MyObject *)mutable1)->_value
nil
As mentioned in the comments this requires discipline in the base class to use the getter instead of the iVar. Many would consider that a good thing, but that debate is off-topic here.
A minor difference you might notice is that I have used the copy attribute for the property. This could be made strong instead with very little change to the code.
How to access instance variable which is defined in class viewController.
ViewController.h
#interface ViewController : UIViewController
{
Class1* class1;
}
ViewController.m
#implementation ViewController
class1 = [[Class1 alloc]init];
#end
Class1.h
#interface Class1 : NSObject
{
NSMutableArray* variablesArray;
}
#property NSMutableArray* variablesArray;
Class1.m
#implementation Class1
#synthesize variablesArray;
#end
Now I would like to access to instance "class1" variable "variablesArray" from Class2.m. I want to add and get objects from MutableArray "variablesArray". Is it possible?
Yes it is Possible.
Make object of other class and access the variable.
In claas2 import Class1 (#import "Class1")
Class1 *objClass1= [[Class1 alloc] init];
now access like
[objClass1.variableArray addObject:#"object"];
[objClass1.variableArray objectAtIndex:0];
If you want to access same instance of array make a Singleton Class
by adding this class method in your class
+ (Class1 *)sharedInstance
{
static Class1 *shared = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,
^{
shared = [[self alloc] init];
});
return shared;
}
and access in Class2 like
[[Class1 sharedInstance] variableAray];
Hey you can access variable in another class by Simple making property of the variable like this
#property(nonatomic, strong) NSString *str;
I gave an Answer close to this please check this link
You can access the public property of a class instance by simple using the dot-operator. Notice that there are several ways to encapsulate an instance variable by setting property attributes like (readwrite, readonly).
Class1 aClass = [[Class1 alloc] init];
aClass.varibalesArray addObject:#[ #"someObject" ];
Make sure you initialize the NSMutableArray correctly in the init method of Class1
- (id)init
{
self = [super init];
if (self) {
_variablesArray = [NSMutableArray array];
}
return self;
}
Newbie to objective C...
NOTE: This is a conceptual problem, as I'm trying to translate "public and private" from what I know about other languages.
How can I access the "stringB" ivar through the "public" method?
myClass.h
#interface myClass : UIViewController {
}
#property (nonatomic, retain) NSString *stringA;
#property (nonatomic, retain) NSString *stringB;
- (void)dealWithStringA;
+ (void)dealWithStringB;
myClass.m
#import "myClass.h"
#interface myClass () {
}
#end
#implementation myClass
// My "private" function
- (void)dealWithStringA
{
return _stringA;
}
// My "public" function
+ (void)dealWithStringB
{
// Errors with: Instance variable "stringB" accessed in class method
return _stringB;
}
The method starting with a + is called a class method in objective C where a method starting with - is an instance method. An instance method can be performed on an instance of that class only.
Also the return type for your method would be an NSString since you are expecting to get a string object from that method.
For a class method, you'll need to create an autoreleasing instance of that class and then perform operations on that instance.
For eg.
+ (NSString*)dealWithStringB
{
MyClass *myClass = [[[MyClass alloc] init] autorelease];
myClass.stringB = #"Its String B";//It's an absurd example
return myClass.stringB;
}
You are wrong with understanding "+", "-" - it's not about private / public.
To have a private function you should implement that in your .m file:
#interface YourClass ()
- (id) privateMethod;
#end
Everything you declare in .h file will be public:
#interface YourClass : NSObject
- (id)someMethod //public
#end
"+" is used for static functions so you can call them without having an instance of a class.
For example in your case:
[myClass dealWithStringB];
and for "-" function you need instance.
[[[myClass alloc] init] dealWithStringA];
The static functions can be used when you don't need any properties from a class or to they are pretty often used to create instances of classes.
The "+" prefix means class method, not public. A "-" stands for instance method, not private.
Both public and private methods can access the private state of the class or instance.
myClass.h (Similar to yours)
#interface myClass : UIViewController
{
}
#property (nonatomic, retain) NSString *stringA;
#property (nonatomic, retain) NSString *stringB;
- (void)dealWithStringA;
+ (void)dealWithStringB;
#end
myClass.m
#implementation myClass
#synthesize stringA ;
#synthesize stringB ;
static myClass* instance = nil;
+(void) dealWithStringB
{
if(instance==nil)
{
instance=[myClass alloc]init];
}
else
{
//Access the field this way
printf("#"The string content is %#",instance.stringB);
}
}
Hope its Clear!!!