I have a shared singleton classNSMutableArray [ICGlobals sharedApplianceCount](first time using this pattern so bear with me if ive done something really silly here)
.h
#import <Foundation/Foundation.h>
#interface ICGlobals : NSObject
{
NSMutableArray* applianceCount;
}
#property (nonatomic, retain) NSString *applianceCount;
+ (ICGlobals *)sharedApplianceCount;
#end
.m
#import "ICGlobals.h"
#implementation ICGlobals
static ICGlobals *sharedApplianceCount = nil;
+ (ICGlobals *)sharedUser {
if(sharedApplianceCount == nil){
sharedApplianceCount = [[super allocWithZone:NULL] init];
}
return sharedApplianceCount;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedApplianceCount];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#end
In "another view controller" im trying to add the row count of my table view (changeable amount of rows) = self.circuits.count
Having tried this
[[ICGlobals sharedApplianceCount] addObject: self.circuits.count,nil]];
and
[[ICGlobals sharedApplianceCount] = [[NSMutableArray alloc] init];
[[ICGlobals sharedApplianceCount] addObject: self.circuits.count,Nil]];
I get no visible #interface error saying my singleton class declares the selector
same with
NSNumber* numberOfRows = [NSNumber numberWithInteger:self.circuits.count];
[[ICGlobals sharedApplianceCount]addObject:[NSMutableArray arrayWithObjects:numberOfRows, nil]];
and with
[ICGlobals sharedApplianceCount] = self.circuits.count;
I get expression assignable. Singleton class has been imported.
You have an inconsistency in your interface declaration. You declare ivar of type NSMutableArray and then a NSString property. Firstable, you don't need to declare ivar, declaring a property does it for you. So your interface should look like:
#interface ICGlobals : NSObject
#property (nonatomic, retain) NSMutableArray *applianceCount;
+ (ICGlobals *)sharedApplianceCount;
#end
Furthermore, you have a naming glitch. You should not use name applianceCount for an array. In general, naming convention of Cocoa suggests that count should be a number (int or NSUInteger). I would change this property name to applianceCounts.
Then, when you initialize your singletone, you can also initialize the array:
+ (ICGlobals *)sharedUser
{
if(sharedApplianceCount == nil)
{
sharedApplianceCount = [[super allocWithZone:NULL] init];
sharedApplianceCount.applianceCounts = [[NSMutableArray alloc] init];
}
return sharedApplianceCount;
}
Finally, here is how to add data to your singletone's applianceCounts array from view controller.
NSNumber* numberOfRows = [NSNumber numberWithInteger:self.circuits.count];
[[ICGlobals sharedApplianceCount].applianceCounts addObject:numberOfRows];
This should point you to right direction.
I don't fully get what you are trying to achieve like I don't understand why you want to have an array there, so if you need further help please let me know in the comments.
I fully recommend you reading about naming conventions. A good start is this article:
Introduction to Coding Guidelines for Cocoa.
I would recommend some refactoring to your class.
First you make the interface like this:
#interface ICGlobals : NSObject
// add the app count but make it private, because you will provide methods to access it
#property (nonatomic, readonly) NSString *applianceCount;
// return ICGlobals instance
+ (ICGlobals)sharedCounter;
- (NSInteger)count;
- (void)addObject:(id)object;
now in .m file
#implementation ICGlobals
static ICGlobals *sharedApplianceCount = nil;
// this is your method, just changed the name
+ (ICGlobals *)sharedCounter {
if(sharedApplianceCount == nil){
sharedApplianceCount = [[super allocWithZone:NULL] init];
}
return sharedApplianceCount;
}
// instance methods goes here
- (NSInteger)count
{
return _applicationCount.count;
}
- (void)addObject:(id)object
{
[_applicationCount addObject:object];
}
Now call [[ICGlobals sharedCount]addObject:object] from any viewController
Related
I am working on a project that contains a Singleton instance of a class called Survey. Within that singleton instance is a property called "itemArray," which is an NSMutableArray that contains any number of instances of an Item class.
My Items class contains several NSInteger properties, but for the purposes of the application, I need to initialize all NSIntegers with a value of -1 instead of the default 0.
Now, for my Survey class (the one with the Singleton instance), I use the following method in the implementation to change the default value of a property:
-(id)init {
if (self = [super init]) {
_thingy = -1;
}
return self;
}
This works, but for some reason, the same exact syntax (with different properties) doesn't work for instances of my Item instances. For what it's worth, the following codeblock is the creation of 2 instances of Item, and their insertion into itemArray. I also tried the Item *item1 = [[Item alloc]init; method to no avail.
Item *item1;
[[[Survey sharedInstance]itemArray]insertObject:item1 atIndex:0];
Item *item2;
[[[Survey sharedInstance]itemArray]insertObject:item2 atIndex:1];
}
I would appreciate any assistance.
!!!!!UPDATE!!!!!
I entered the following conditional:
if (![[Survey sharedInstance]itemArray]){
NSLog(#"Test");
}
And the "test" logged onto the console, so it looks like the itemArray isn't being initialized. I'm not sure how to actually initialize it, though. When I try the following:
[[Survey sharedInstance]addressArray] = [[NSMutableArray alloc]init];
I'm getting an error saying "Expression is not assignable."
Survey.h:
#import <Foundation/Foundation.h>
#interface Survey : NSObject
+(instancetype)sharedInstance;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *emailAddress;
#property (nonatomic, assign) NSInteger trigger1;
#property (nonatomic, assign) NSInteger trigger2;
#property (nonatomic, assign) NSInteger trigger3;
#property (nonatomic, assign) NSInteger activeItem;
#property (nonatomic, strong) NSMutableArray *itemArray;
#end
Survey.m
#import "Survey.h"
#implementation Survey
+ (instancetype)sharedInstance {
static Survey *_instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[Survey alloc] init];
});
return _instance;
}
-(id)init {
if (self = [super init]) {
_storage = -1;
}
return self;
}
#end
Instead of
[[Survey sharedInstance]addressArray] = [[NSMutableArray alloc]init];
you need to use
[Survey sharedInstance].addressArray = [[NSMutableArray alloc]init];
or
[[Survey sharedInstance] setAddressArray:[[NSMutableArray alloc]init]];
You were trying to assign a value to the return value of a getter method, which is not possible, so the compiler was saying "Expression not assignable." You need to use the setter method or dot notation instead.
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...
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;
}
Goal of program: Enter numbers on a viewController. When the user hits Submit button, the data entered by user is passed on to a different class for displaying on a different viewController.
Problem: I am trying to access an instance variable (numberList) in an instance method (-(void)insertNewNumber:(Numbers *)tempNumber), but it never gives me the correct output. But when I access the same variable through a protocol method of UITableViewDataSource, I get the correct answer. I figured this by using NSLog in instance method and protocol method.
Since I have declared numberList as a property variable, I was thought that I can access it from anywhere in the program and get the correct value stored in it. But compiler returned 0 for the NSLog statements when they were called from instance method. When the NSLog statements from protocol method, showed the correct result.
Please help me understand why is this occurring and how can I add elements into an array from any method in a program.
Thank you!
Here's the relevant code I am working on:
Numbers.h:
#interface Numbers:NSObject
#property (strong, retain) NSString *ID;
#property (strong, retain) NSInteger *number;
#end
Numbers.m
#implementation Numbers
#synthesize ID, number;
#end
DisplayNumbers.h
#import <UIKit/UIKit.h>
#import "Numbers.h"
#interface DisplayNumbers : UIViewController <UITableViewDataSource, UITableViewDelegate>
#property (strong, nonatomic) NSMutableArray *numberList;
- (void)insertNewNumber:(Numbers *)tempNumber;
#end
DisplayNumbers.m:
#implementation DisplayNumbers
#synthesize numberList;
- (void)viewDidLoad
{
[super viewDidLoad];
numberList = [[NSMutableArray alloc] init];
Numbers *num0 = [[Numbers alloc] init];
Numbers *num1 = [[Numbers alloc] init];
num0.ID = [NSString stringWithFormat:#"ID 0"];
num0.number = 1111111111;
num1.ID = [NSString stringWithFormat:#"ID 1"];
num0.number = 2222222222;
[numberList addObject:num0];
[numberList addObject:num1];
}
- (void)insertNewNumber:(Numbers *)tempNumber
{
NSLog(#"numberList.count (in -(void)insetNewNumber) = %d", numberList.count);
[numberList addObject:tempNumber];
NSLog(#"numberList.count (in -(void)insetNewNumber) = %d", numberList.count);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"numberList.count (in -(NSInteger)tableView:...) = %d", numberList.count);
Numbers *temp = [[Numbers alloc] init];
temp.ID = #"hi";
temp.Number = 1234;
[numberList addObject:temp];
NSLog(#"numberList.count (in -(NSInteger)tableView:...) = %d", numberList.count);
return numberList.count;
}
#end
Edit 1: Calling of insertNewNumber:.
This method is being called from a different class.
InputNumber.h:
#import <UIKit/UIKit.h>
#import "DisplayNumbers.h"
#interface InputNumber:UIViewController
#property (retain, strong) NSInteger *enteredNumber;
-(void)enteredNumber;
#end
InputNumber.m
#implementation InputNumber
#synthesize enteredNumber;
-(void)enterNumber
{
DisplayNumber *temp = [[DisplayNumber alloc] init];
[temp insertNewNumber:enteredNumber];
}
#end
Since you allocate your numberList in the ViewDidLoad method, be sure to call your insertNewNumber method after the call to ViewDidLoad.
I believe that
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Numbers *temp = [[Numbers alloc] init];
temp.ID = #"hi";
temp.Number = 1234;
[self insertNewNumber:temp];
return contactList.count;
}
works, right?
If you need to call your insertNewNumber method before the call to ViewDidLoad, allocate your numberList
numberList = [[NSMutableArray alloc] init];
in an overloaded initWithNibName:bundle: method.
Your code doesn't have an ivar called numberList. You need to call the property like this:
self.numberList = [NSMutableArray array];
No need to alloc/init since you're already using a strong reference.
Every time you refer to that numberList object, you need to use self.numberList.
[self.numberList addObject:num0];
[self.numberList addObject:num1];