Cannot access Objective-C singleton's array from Swift code - objective-c

I made an array in a singleton to write objects into it from multiple parts of my code. Here's how:
// in singleton.h
#import <UIKit/UIKit.h>
// make globally accessible array
#interface MyManager : NSObject {
NSMutableArray *imgArray;
}
#property (nonatomic, retain) NSMutableArray *imgArray;
+ (id)sharedManager;
#end
// in singleton.m
#import "singleton.h"
For my .m file :
#implementation MyManager
#synthesize imgArray;
#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]) {
self.imgArray = [NSMutableArray new];
}
NSLog(#"initialized");
return self;
}
#end
I can access my array called imgArray it from my objective C code. However, In swift I get an error when I do this:
let array = MyManager.sharedManager()
array.imgArray.add("hello world") . (!!!) Value of type 'Any?' has no member 'imgArray'
I can access MyManager.sharedManager(), but Why can't I access imgArray the same way as in objective C?

You should declare it as instancetype or MyManager *. E.g.
+ (MyManager *)sharedManager;
or
+ (instancetype)sharedManager;
A couple of suggestions:
The Swift convention for singleton’s is to use a class property name of shared, not a class method of sharedManager(). When you declare it in Objective-C, you might want to explicitly say that it’s a class property:
#property (class, readonly, strong) MyManager *sharedManager NS_SWIFT_NAME(shared);
This won’t change any of the Objective-C behavior, but in Swift, you can just do:
let manager = MyManager.shared
manager.images.add(image)
This results in more concise and idiomatic Swift code.
I’d suggest that you audit your Objective-C for nullability. I.e., confirm what can be nil and what can’t. Since both imgArray (which I might just call images) and sharedManager can never be nil, I would just use the NS_ASSUME_NONNULL_BEGIN/END macros which tells the compiler “unless I tell you otherwise, assuming this property cannot be nil”:
// MyManager.h
#import UIKit;
NS_ASSUME_NONNULL_BEGIN
#interface MyManager : NSObject
#property (nonatomic, strong) NSMutableArray <UIImage *> *images;
#property (class, readonly, strong) MyManager *sharedManager NS_SWIFT_NAME(shared);
#end
NS_ASSUME_NONNULL_END
By telling the compiler that these two cannot be nil, that means that you’ll have to do less unnecessary unwrapping of optionals in your Swift code.
As an aside, notice that I didn't declare an instance variable. (And if you did need one, I wouldn’t advise declaring it in the public interface.) Objective-C will now synthesize the ivars backing our properties automatically for us. (So my property images will have an ivar called _images that will be synthesized for me.) And you don’t need/want the #synthesize line, either:
// MyManager.m
#import "MyManager.h"
#implementation MyManager
+ (instancetype)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (instancetype)init {
if (self = [super init]) {
self.images = [NSMutableArray new];
}
NSLog(#"initialized");
return self;
}
#end

Change + (id)sharedManager; to + (MyManager *)sharedManager;. Otherwise Swift doesn't know what kind of object sharedManager is and it will assume it's Any.

Related

How to reference member variable in the implementation file

I am learning Objective-C and I am trying to split the class definition from the implementation as shown below.
Now in the code I want to reference the both of:
NSString *CarMotorCode;
NSString *CarChassisCode;
In the implementation file. I attempted to use:
self.CarMotorCode;
self.CarChassisCode;
But it does not work. Would you please let me know how to reference it.
Note: please let me know what is the right naming convention for the variables enclosed inside the brackets in the implementation section? Are they member variables?
Car2.m:
#import <Foundation/Foundation.h>
#import "Car2.h"
#implementation Car2
-(id) initWithMotorValue:(NSString *)motorCode andChassingValue:(NSInteger)ChassisCode {
self
}
#end
Car2.h
#ifndef Car2_h
#define Car2_h
#interface Car2 : NSObject {
NSString *CarMotorCode;
NSString *CarChassisCode;
}
-(id) initWithMotorValue: (NSString *) motorCode andChassingValue: (NSInteger) ChassisCode;
-(void) startCar;
-(void) stopCrar;
#end
#endif /* Car2_h */
You have declared instance variables (ivars). To get the “dot syntax”, you need to declare properties. The “dot syntax” is syntactic sugar that makes use of the “accessor methods” that are synthesized for you when you declare a property. (FWIW, it’s advised to not declare ivars manually, anyway, and rather to declare properties and let the compiler synthesize the necessary ivars. See Programming with Objective-C: Properties Control Access to an Object’s Values and Practical Memory Management: Use Accessor Methods to Make Memory Management Easier.)
Thus:
#interface Car2: NSObject
#property (nonatomic, copy) NSString *motorCode;
#property (nonatomic, copy) NSString *chassisCode;
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode;
#end
And your init method might look like:
#implementation Car2
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode {
if ((self = [super init])) {
_motorCode = [motorCode copy];
_chassisCode = [chassisCode copy];
}
return self;
}
#end
That will synthesize ivars _motorCode and _chassisCode for you behind the scenes, but you generally wouldn’t interact directly with them (except in init method, in which case you should avoid accessing properties). But in the rest of your instance methods, you could just use the properties self.motorCode and self.chassisCode.
A few unrelated notes:
I dropped the car prefix in your property names. It seems redundant to include that prefix when dealing with a car object.
I start my property names with lowercase letter as a matter of convention.
I changed the init method signature to better mirror the property names (e.g. not initWithMotorValue but rather initWithMotorCode).
Alternatively, you might use the strong memory qualifier rather than copy. E.g.
#interface Car2: NSObject
#property (nonatomic, strong) NSString *motorCode;
#property (nonatomic, strong) NSString *chassisCode;
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode;
#end
And
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSString *)chassisCode {
if ((self = [super init])) {
_motorCode = motorCode;
_chassisCode = chassisCode;
}
return self;
}
But we often use copy to protect us against someone passing a NSMutableString as one of these properties and then mutating it behind our back. But this is up to you.
You defined chassisCode to be a string in your ivar declaration, but as an NSInteger in your init method signature. Obviously, if it’s an NSInteger, change both accordingly:
#interface Car2: NSObject
#property (nonatomic, copy) NSString *motorCode;
#property (nonatomic) NSInteger chassisCode;
- (id) initWithMotorCode:(NSString *)motorCode chassisCode:(NSInteger)chassisCode;
#end
and
- (id)initWithMotorCode:(NSString *)motorCode chassisCode:(NSInteger)chassisCode {
if ((self = [super init])) {
_motorCode = [motorCode copy];
_chassisCode = chassisCode;
}
return self;
}
If you’re wondering why I didn’t use the property accessor methods in the init method, please see Practical Memory Management: Don’t Use Accessor Methods in Initializer Methods and dealloc.

expected ';' at end of declaration list objective c

"error: expected ';' at end of declaration list" objective c
When I am trying to declare global variable
#implementation CachingManager{
NSMutableArray*object = [[NSMutableArray alloc] init];
}
You can create instance variables here but cannot initialize those instance variables here like you do. They are all initialized to nil or zeroes. So compiler expect a semicolon when you are writing an equal sign.
You can initialize them in init method or other method where your class initialization takes place in order to make them global variables. e.g.
Interface block for instance variable inside .m file:
#interface CachingManager ()
{
// instance variables initialized to nil or zeroes
NSMutableArray *object; // global ivar
}
#end
Implementation part same .m file:
#implementation CachingManager
- (void)viewDidLoad {
[super viewDidLoad];
object = [[NSMutableArray alloc] init]; // initialization takes place
}
One way to implement global variables, and to manage their lifetime (i.e. that they are initialised) and even to provide global methods is to implement a class exposing those variables/methods and to use the singleton pattern:
yourFile.h:
#import <Foundation/Foundation.h>
#interface GlobalVars : NSObject
{
NSMutableArray *_truckBoxes;
NSMutableArray *_farmerlist;
NSString *_farmerCardNumber;
NSString *_fName;
}
+ (GlobalVars *)sharedInstance;
#property(strong, nonatomic, readwrite) NSMutableArray *truckBoxes;
#property(strong, nonatomic, readwrite) NSMutableArray *farmerList;
#property(strong, nonatomic, readwrite) NSString *farmerCardNumber;
#property(strong, nonatomic, readwrite) NSString *fName;
#end
yourFile.m:
#import "GlobalVars.h"
#implementation GlobalVars
#synthesize truckBoxes = _truckBoxes;
#synthesize farmerList = _farmerList;
#synthesize frameCardNumber = _frameCardNumber;
#synthesize fName = _fName;
+ (GlobalVars *)sharedInstance {
static dispatch_once_t onceToken;
static GlobalVars *instance = nil;
dispatch_once(&onceToken, ^{
instance = [[GlobalVars alloc] init];
});
return instance;
}
- (id)init {
self = [super init];
if (self) {
_truckBoxes = [[NSMutableArray alloc] init];
_farmerlist = [[NSMutableArray alloc] init];
// Note these aren't allocated as [[NSString alloc] init] doesn't provide a useful object
_farmerCardNumber = nil;
_fName = nil;
}
return self;
}
You can then use these global variables like this, for example:
GlobalVars *globals = [GlobalVars sharedInstance];
globals.fName = #"HelloWorld.txt";
[globals.farmerList addObject:#"Old Macdonald"];
[self processList:[globals farmerList]];
However, please consider:
You don't need to use global variables like this; you should be able to create a model object which is created as necessary and reference to it passed to the views. This is MVC.
You also posted a stack trace of an unrelated issue which is extremely common with Objective-C; only you can fix this error, once you realise what it is.
That sin't a global variable. That would be an instance variable and that particular syntax wasn't commonly used after 2005 (really, it wasn't terribly common after the mid 90s).
If you want a global variable, do:
NSMutableArray *myGlobal;
Somewhere at the top level -- at the same level with the #implementation -- of your source.
You'll have to initialize the global variable elsewhere, though. Typically, in the +initialize or +load method of the class.

Appending to an array in an object from a ViewController

I would like to append an object to an NSMutableArray in an object class from a ViewController. It's set up like bellow, but the code below does not seem to work. If I log the array from the ViewController, it appears to be appended, but if I log it from the object class, it's empty.
CaptureManager.h
#import <UIKit/UIKit.h>
#class AVCamRecorder;
#protocol CaptureManagerDelegate;
#interface CaptureManager : NSObject {
}
#property (nonatomic,strong) NSMutableArray *assets;
#end
ViewController
CaptureManager *cm = [[CaptureManager alloc] init];
cm.assets = [[NSMutableArray alloc]init];
[cm.assets addObject:asset];
Alternatively, just pass in a specific instance of CaptureManager to the VC, either through an initializer or by creating a CaptureManager property on the VC and setting it to the specific instance of CaptureManager.
You can (and should) read all about why you should avoid singleton abuse here.
It's because in your view controller you're creating a new object of CaptureManager. So you must pass a pointer of already created CaptureManager and use that, or have a shared instance (singleton) and use that in your view controllers, e.g.
#interface CaptureManager : NSObject
+ (instancetype)sharedManager;
#property (nonatomic,strong) NSMutableArray *assets;
#end
//--
#implementation
+ (instancetype)sharedManager
{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [self new];
});
return instance;
}
- (id)init
{
self = [super init];
self.assets = [NSMutableArray new];
return self;
}
Then, in your vc:
[[CaptureManager sharedManager].assets addObject:asset];

Access Class without initializing

I want to create a class in objective-c with its methods, so that for accessing the data I don't want to instantiate the class. how can I do it?
Either you can use singleton, or if you are planning to use only static methods, you can just add it in the class and use it directly with class name.
Create methods as static,
+(void)method;
then use it as,
[MyClass method];
This is helpful only if you are creating some utility classes which has only some utility method like processing an image or so. If you need to have property variables, you will need singleton.
For eg:-
Go to new file and create MySingleton class which will create MySingleton.h and MySingleton.m files.
In .h file,
#interface MySingleton : NSObject
{
UIViewController *myview;
}
#property (nonatomic, retain) UIViewController *myview;
+(MySingleton *)sharedSingleton;
In .m file,
+ (MySingleton*)sharedSingleton {
static MySingleton* _one = nil;
#synchronized( self ) {
if( _one == nil ) {
_one = [[ MySingleton alloc ] init ];
}
}
return _one;
}
- (UIViewController *)myview {
if (!myview) {
self.myview = [[[UIViewController alloc] init] autorelease]; //you can skip this, but in that case you need to allocate and initialize the first time you are using this.
}
return myview;
}
Then use it as,
[[MySingleton sharedSingleton] myview] anywhere in your project. Remember to import MySingleton.h though. Similarly you can create any object in singleton and use it. Just implement the getter or setter method accordingly.
One thing you have to be careful is that the object created in a singleton has only a single memory space allocated and hence it is the same object whenever you are using anywhere in your project. The above code will not create multiple copies of myview object in the class. So whenever you are modifying a property of myview that will be reflected everywhere. Use this approach only if it is absolutely needed and you need to have access to a single object from all over the project. Normally we use this only for situations like storing a sessionID which needs to be accessed from different classes etc..
You may use singleton pattern, check this question.
Like this:
+(MySingleton *)sharedInstance {
static dispatch_once_t pred;
static MySingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
shared.someIvar = #"blah";
});
return shared;
}
Or if you want to just access methods, you may use factory methods (those with +, not with -)
#interface MyClass
#property (nonatomic, assign) NSInteger value;
+ (void) factoryMethod;
- (void) instanceMethod;
...
// then in code
[MyClass factoryMethod]; // ok
[[MyClass sharedInstance] instanceMethod]; // ok
[MyClass sharedInstance].value = 5; // ok
UPDATE:
You may add a property to appDelegate
// in your app delegate.h
#property (nonatomic, retain) UIViewController* view;
// in your app delegate.m
#synthesize view;
and get appDelegate from almost any place like:
myapp_AppDelegate* appDelegate = [[UIApplication sharedApplicaton] delegate];
appDelegate.view = ...; // set that property and use it anywhere like this
Note, that you'll need to #import your UIViewController subclass and your appDelegate.h to make autocomplete work and sometimes avoid warnings.
// someFile.m
#import "appDelegate.h"
#import "myViewController.h"
...
myapp_AppDelegate* appDelegate = [[UIApplication sharedApplicaton] delegate];
appDelegate.view.myLabel.text = #"label text";

Objective-C Where to define initial Class Properties

just wanted to ask where I define initial class properties?
From other languages I am used to define some standard properties in the head before the content of the class starts.
For example paths to files. Settings and so on.
Where I fill these initial properties with values in Objective-C?
Thanks
Generally it's something like:
MyClass.h:
extern NSString * const staticValue1;
extern NSString * const staticValue2;
#interface MyClass : NSObject
{
NSString *_strval;
int _intval;
float _fltval;
}
#property (retain, nonatomic, readwrite) NSString *strval;
#property (assign, nonatomic, readwrite) int intval;
#property (assign, nonatomic, readwrite) float fltval;
#end
MyClass.m:
NSString * const staticValue1 = #"Something";
NSString * const staticValue2 = #"Something else";
#interface MyClass
#synthesize strval = _strval;
#synthesize intval = _intval;
#synthesize fltval = _fltval;
- (id)init
{
self = [super init];
if (self != nil)
{
[self setStrval:[NSString stringWithFormat:#"This is a %#", #"string"]];
[self setIntval:10];
[self setFltval:123.45f];
}
return self;
}
- (void)dealloc
{
[self setStrval:nil];
[super dealloc];
}
#end
This demonstrates the use of synthesized properties which are being used here to manage the memory of the instance variable _strval, which requires retaining/releasing to avoid memory leaks. Note that [self setStrval] is initialised with an autoreleased object (from [NSString stringWithFormat) and will be retained by the setter method. Alternatively these methods can be called using the following syntax, if you prefer:
self.strval = [NSString stringWithFormat:#"This is a %#", #"string"];
self.intval = 10;
self.fltval = 123.45f;
Maybe some of what you are after can be implemented with class methods.
Class methods are coded with a + (instead of the instance methods' -), and can't refer to instance variables, as they are not associated with any specific instance of the class.
This is a class method to return a default string:
+ (NSString *)myDefaultString
{
return #"Some default value";
}
You call it by simply calling it with the class name at the receiver's place. Imagine you have defined the method in a class called MyClass, the you call it like this:
NSString *str = [MyClass myDefaultString];
You'll notice that there is no alloc/init calls in this.
Public property needs to be define in .h file.
#interface MyClass {
}
#property(nonatomic, reatin) NSString *a;//Define as per needs, then synthesise in .m file
#end
For private property you need define inline category in .m file-
#interface MyClass ()
#property(nonatomic, reatin) NSString *b;//Define as per needs, then synthesise in .m file
#end
#implementation MyClass
#synthesize a = _a;
#synthesize b = _b;
- (void)viewDidLoad {
//You can initialise property here or in init method
self.a = #"Demo1";
self.b = #"Demo2";
}
//Now you can have other code for this class.
#end