Appending to an array in an object from a ViewController - objective-c

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];

Related

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

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.

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.

Singleton set in AppDelegate loses it value when allocated in another class

I have a iPad application where I'm attempting to use a singleton. This is the code in the .h file:
//-------------------------------------------
//-- singleton: timeFormat
#interface SingletonTimeFormat : NSObject {
}
#property (nonatomic, retain) NSNumber *timeFormat;
+ (id)sharedTimeFormat;
#end
This is the code from the .m file:
//-------------------------------------------
//-- SingletonTimeFormat
#implementation SingletonTimeFormat {
}
#synthesize timeFormat;
//-- sharedColorScheme --
+ (id)sharedTimeFormat {
static dispatch_once_t dispatchOncePredicate = 0;
__strong static id _sharedObject = nil;
dispatch_once(&dispatchOncePredicate, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
-id) init {
self = [super init];
if (self) {
timeFormat = [[NSNumber alloc] init];
}
return self;
}
#end
I load the value (either 12 or 24) in AppDelegate - didFinishLaunchingWithOptions, then when I want to get the value of timeFormat I use this:
SingletonTimeFormat *stf = [[SingletonTimeFormat alloc]init];
if([stf.timeFormat isEqualToNumber: [NSNumber numberWithInt:12]]) {
which returns 0 (it was set correctly in AppDelegate, but apparently when I do the alloc in another class, it loses it's value. So obviously it's not working! (I have several other singletons that have the same pattern, but so far they appear to be working.
What's wrong here and how do I fix it?
You don't want to call your singleton using alloc init. With this singleton, all references to it should be through its sharedTimeFormat method, which will init the object if necessary, and will return the singleton instance otherwise.
In other words, it doesn't appear that you're referencing the instance of the object stored in the static sharedObject variable, which means that it's stored value will not necessarily be the same.

How to init objects in Objective-C [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm unsure how I should initialise the various properties in an objective-C class. Please assume I'm a very new user to Objective-C in your answers...
I have the following classes:
Test class
#interface Test : NSObject
#property (nonatomic, strong) NSString *name;
#end
TestManager class
#interface TestManager : NSObject
#property (nonatomic, strong) NSMutableArray *tests; // array of Test objects (array size unknown until runtime)
#end
Controller class
#interface TestController : NSObject
#property (nonatomic, strong) TestManager *aManager;
-(void)initManager;
-(void)doSomething;
#end
I want to have an method like initManager called:
-(void)initManager
{
// how can I init the aManager which will have an array of Test objects
}
which will automatically allocate an array of objects to be stored inside the manager class so I can do things like:
-(void)doSomething
{
NSString *name = ((Test *)[self.aManager.tests objectAtIndex:0]).name;
}
I'm not even sure that initManager is the correct method to use - is there something built in that always gets called?
Firstly, let's look at the way we can initialize your Test class objects.
You can also write some initialization method for your Test class so instead of this:
Test example = [[Test alloc] init];
example.name = #"s";
you can write something like this:
Test example = [[Test alloc] initWithName:#"s"];
Please note that this is very common for initialization method to return newly created object, hence the initialization method usually returns 'id' type (not void).
This is the implementation for your test class which will be used in examples below.
.h file:
- (id)initWithName:(NSString *)aName;
.m file:
- (id)initWithName:(NSString *)aName
{
self = [super init];
if (self) {
_name = aName;
}
return self;
}
You can initialize your TestController class this way:
.h file:
- (id)initManager;
.m file:
- (id)initManager
{
self = [super init]; //always call the superclass init method when your class inherit from other class
if (self) { // checking if the superclass initialization was fine
_tests = [NSMutableArray array];
[_tests addObject:[[Test alloc] initWithName:#"s"]];
[_tests addObject:[[Test alloc] initWithName:#"l"]];
}
return self;
}
Or something like this:
- (id)initManager
{
self = [super init]; //always call the superclass init method when your class inherit from other class
if (self) { // checking if the superclass initialization was fine
_tests = [NSArray arrayWithObjects:[[Test alloc] initWithName:#"s"], [[Test alloc] initWithName:#"l"]];
}
return self;
}
Like the #Andrew said it is better to use alloc + init. Here are some examples of this syntax:
CGRect rect = CGRectMake(0, 0, 100, 100);
[[UIView alloc] initWithFrame:rect];
[[NSArray alloc] init]
This is the common way to initialize objects. Despite having this mechanism there are also some additional methods (which are in fact static functions) which give the programmer the nice way to initialize objects. Using them u don't have to write keyword 'alloc' so that the code is shorter and easier to read.
[NSArray array] //creates and returns empty array
[NSMutableArray array] //creates and return empty mutable array
[UIButton buttonWithType:UIButtonTypeContactAdd]; //creates and return button
first import header files of test, and test manager class, into controller class
#import Test.h
#import TestManager.h
then in controller class
-(void)initManager
{
TestManager *aTestManager = [TestManager new];
Test *test1 = [Test new];
Test *test2 = [Test new];
[aTestManager.tests addObject:test1];
[aTestManager.tests addObject:test2];
}
Let's start at the top. You probably can and should make the name readonly.
(Demos assume ARC is enabled)
#interface Test : NSObject
#property (nonatomic, readonly) NSString *name;
// and then simply initialize name:
- (instancetype)initWithName:(NSString *)pName;
#end
NSString properties should be copied:
#implementation Test
- (instancetype)initWithName:(NSString *)pName
{
self = [super init];
if (nil == self) return nil;
// copy the NSString:
// don't use getters/setters in initializers or -dealloc
_name = pName.copy;
return self;
}
#end
Similarly readonly:
#interface TestManager : NSObject
#property (nonatomic, strong, readonly) NSMutableArray *tests; // array of Test objects (array size unknown until runtime)
#end
#implementation TestManager
- (id)init
{
self = [super init];
if (nil == self) return nil;
// just initialize readonly tests:
_tests = NSMutableArray.new;
return self;
}
#end
Then TestController could probably use a readonly TestManager and borrow the form used above. Otherwise, it can be readwrite, if needed.
// don't declare/implement an instance method
// which has the prefix -init*, like initManager. renamed.
- (void)resetManager
{
// if readonly is ok, then just create it in the initializer.
// otherwise, if you need the ability to set the manager in the controller,
// then declare the property readwrite and:
self.testManager = TestManager.new;
// note: aManager is not a good name. renamed to testManager.
}
- (void)doSomething
{
assert(self.testManager && "did you forget to init the manager?");
Test * test = [self.testManager.tests objectAtIndex:0];
NSString * name = test.name;
...
}
This is far from covering all initialization cases in ObjC, but it is a start.

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";