Forward Declaration Stalls App - ios

So I was having issues where I had circular dependencies and so I am trying to implement forward declaration. I have three classes (logger, api, helpers), all of which are singleton objects. I have one controller class (globalclass), that has properties linking to the three classes.
Here is my code:
includes.h:
#import "globalclass.h"
#import "logger.h"
#import "api.h"
#import "helpers.h"
globalclass.h:
#class logger
#class api
#class helpers
#interface globalclass : NSObject
+ (id) sharedGlobal;
- (id) init;
...
#property (nonatomic, strong) logger *log;
#property (nonatomic, strong) api *remote;
#property (nonatmoic, strong) helpers *utils;
...
logger/api/helpers.h templates:
#class globalclass
#interface logger : NSObject
+ (id) sharedLogger;
- (id) init;
...
#property (nonatomic, strong) globalclass *gc;
...
globalclass.m:
#import "includes.h"
#implementation globalclass
/*
* Singleton Class
*/
+ (id) sharedGlobal
{
static globalclass *sharedGlobalClass = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedGlobalClass = [[self alloc] init];
});
return sharedGlobalClass;
}
/*
* Init
*/
- (id) init
{
self = [super init];
if (!self) return nil;
// Logging
self.logger = [logger sharedLogger];
...
return self;
}
logger/api/helpers.m template:
#import "includes.h"
#implementation logger
/*
* Singleton Class
*/
+ (id) sharedLogger
{
static logger *sharedLog = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedLog = [[self alloc] init];
});
return sharedLog;
}
/*
* Init
*/
- (id) init
{
self = [super init];
if (!self) return nil;
// Global Class
self.gc = [GlobalClass sharedGlobal];
...
return self;
}
Then, in my AppDelegate, I have a property to the global class and am doing this:
self.gc = [GlobalClass sharedGlobal];
However, it seems that everytime I boot up my app, it stalls and is automatically terminated because it takes too long to boot up.
Am I thinking about forward globals incorrectly?
The app builds fine.
Any help would be appreciated.
Thanks.

You have a deadlock, because the dispatch_once block in sharedGlobal is (via the global class init) invoking sharedLogger, which itself is invoking sharedGlobal (via the logger class init).
Since everything is singletons, there are a few approaches you could take to avoid this issue. Here are two:
A. Replace any self.log usages in the global class with [logger sharedLogger], and replace any self.gc usages in the logger class with [globalclass sharedGlobal]. You can get rid of these properties entirely. dispatch_once is very fast after the first invocation, so performance is not a reason to store reference to singletons as properties (though you could conceivably have other reasons)
B. Keep the log and gc properties, but make them readonly properties with custom getters:
#property (nonatomic, readonly) globalclass *gc;
// implementation:
- (globalclass *)gc
{
return [globalclass sharedGlobal];
}
#property (nonatomic, readonly) logger *log;
// implementation:
- (logger *)log
{
return [logger sharedLogger];
}
Personally I would choose A.
Both of these alternatives eliminate any references to the singleton accessors from the init methods, which gets rid of the deadlock.

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.

Is there any reason a static variable could not be initialized?

I have a class (singleton class) that has three global static variables. Each of these static variables has the same type (another class that has a custom init method).
When I want to initialize the static variables, one of them has the value "nil" even if the custom init method returns a valid instance.
Please consider the above code.
//The header file of the singleton class
#import "B.h"
static B *firstVariable;
static B *secondVariable;
static B *thirdVariable;
#interface A : NSObject
//some properties and methods declarations here
#end
//The implementation file of the singleton class
#import "A.h"
#implementation A
static A *sharedInstance = nil;
+ (A *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[A alloc] init];
}
return sharedInstance;
}
...
#end
//The header file for the other class
#interface B : NSObject
#property(nonatomic, strong, readonly) NSString *path;
- (instancetype)initWithPath:(NSString *)path;
#end
//The implementation of this class
#import "B.h"
#implementation B
- (instancetype)initWithPath:(NSString *)path {
self = [super init];
if (self) {
_path = path;
}
return self;
}
#end
This is how I try to initialize those static variables
firstVariable = [[B alloc] initWithPath:#"firstPath"];
secondVariable = [[B alloc] initWithPath:#"secondPath"];
thirdVariable = [[B alloc] initWithPath:#"thirdPath"];
As I previously mentioned, even if there is a valid instance inside of the "initWithPath" method before returning, the "secondVariable" has the "nil" value.
Can anyone help me out with this?

Singleton object gets set for some reason

I've got a singleton class named MyManager which holds multiple of properties. One of these properties is a custom class I've made that inherits from NSObject and also holds multiple properties.
One of my ViewControllers changes one of the properties in the MyManager class but for some reason, it also changes a property (with the same name), but inside the custom object.
To explain:
MyManager:
Custom object
objectThatGetsSet (inside custom object)
objectThatGetsSet
Once I set the objectThatGetsSet in MyManager, it also get's set inside the custom object. Why is this and how can I avoid it?
Here's the MyManager header file:
#import <Foundation/Foundation.h>
#import "myClass.h"
#interface MyManager : NSObject
#property (nonatomic, strong) NSDate *objectThatGetsSet;
#property (nonatmoc, strong) myClass *customObject;
+(id)sharedManager;
#end
And here's the implementation file:
#import "MyManager.h"
#implementation MyManager
#synthesize objectThatGetsSet, customObject;
#pragma mark Singleton Methods;
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
-(id)init {
if (self == [super init]) {
}
return self;
}
#end
Here's the custom class header file:
#import <Foundation/Foundation.h>
#interface myClass : NSObject
#property (nonatomic, strong) myClass objectThatGetsSet;
#end
And the implementation file:
#import "WorkLocationModel.h"
#implementation WorkLocationModel
-(id)init {
// Init self
self = [super init];
if (self)
{
// Setup
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.objectThatGetsSet forKey:#"objectThatGetsSet"];
}
-(instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self)
self.objectThatGetsSet = [coder decodeObjectForKey:#"objectThatGetsSet"];
return self;
}
#end
Please help me with this, thanks a lot!

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...

objective c - Access private var from public function

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!!!

Resources