I declared this array in my view controller implementation file:
NSMutableArray *images = [NSMutableArray array];
I wanted an empty, mutable array that I would later add UIImageViews to. It always return the error:
initializer element is not constant
The proper solution is to make images an instance variable and then you initialize it in your init method.
#implementation SomeClass {
NSMutableArray *images; // instance variable
}
- (id)init {
self = [super init];
if (self) {
images = [[NSMutableArray alloc] init];
}
return self;
}
This is an example. If you have a specific init... method, use that instead.
As an instance variable, other methods in the class now have access to images and each instance of the class gets its own copy of images.
You need to show more code, but the problem is pretty obvious if that really is the line that is erroring out.
You can only dynamically initialization variables at the time of declaration in very specific spots. Dynamically includes calling a method.
NSMutableArray *a = [NSMutableArray array]; // this will error.
static NSMutableArray *a = [NSMutableArray array]; // this will error.
#implementation Booger
{
NSMutableArray *a = [NSMutableArray array]; // this will error
}
NSMutableArray *a = [NSMutableArray array]; // this will error.
- (void)bar
{
NSMutableArray *a = [NSMutableArray array]; // this is fine
}
Sounds like you need to dive a bit more deeply on the whole object-oriented thing. A class is a collection of functions called methods that either operate on the class (class methods) or a single instance of the class (instance methods). An instance can store state that is accessible to all methods when any method is invoked on that instance. In traditional OO, such state is stored in instance variables. Typically, you would define a pair instance methods that set and get that instance variable's value. These are called accessors or setter/getter. In modern Objective-C, we use properties to declare both the instance variables and the methods that access the instance variable.
Thus:
#interface MyClass:NSObject
#property(strong) NSMutableArray *myArray;
#end
#implementation MyClass
// the #property will automatically create an instance variable called _myArray,
// a getter method called -myArray and a setter called -setMyArray:
- init
{
self = [super init];
if (self) {
_myArray = [NSMutableArray array]; // set the ivar directly in init
}
return self;
}
- (void)maybeAddThisThing:(Thing *)aThing
{
if ([aThing isCool] && ![[self myArray] containsObject:aThing]) {
[[self myArray] addObject:aThing];
}
}
- (void)nukeFromOrbit
{
[self setMyArray:[NSMutableArray array]];
// or you could do [[self myArray] removeAllObjects];
}
The return of your NSMutableArray construction does not have an address known at compile time. You can only initialise dynamically inside method scope.
Static initialisation would be fine though: For example, NSString *myString = #"Hello String"; in global scope will compile just fine.
Related
In objective C, its common practice to instantiate internal class arrays (and the like) in a lazy manner.
So if you call on the getter, it first checks if the array isn't nil, and allocates memory for it if needed.
But what about the setter?
If you are trying to insert some value into one of the array cells, since we did not allocate memory for it yet - where does it go?
I'm missing something here, clearly. Would be happy for a clarification.
I'm not sure I understand your question, but if you do this:
#property (nonatomic, strong) NSMutableArray* myArray;
...
- (NSMutableArray *) myArray {
if(!_myArray) {
NSLog(#"created");
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
...
[self.myArray addObject:#"test"];
The getter is actually getting called when you call addObject:, so you'll see "created" being logged.
So #property declarations are syntactic sugar for declaring, in the case of objects, pointers to instance variables. The "nonatomic" refers to the type of getter and setter automatically created (in this case "non thread safe.") And the "strong" is an indicator to ARC to increase the retain count of the variable.
So when you declare:
#property (nonatomic, strong) NSMutableArray* myArray;
This is what really gets created in your class - just a pointer to your hidden instance variable.
#implementation MyClass {
NSMutableArray *_myArray;
}
As you can see in the getter, you are initializing the _myArray pointer to point to a new NSMutableArray:
- (NSMutableArray *) myArray {
if(!_myArray) {
NSLog(#"created");
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
However in the setter, you are just updating the pointer to a variable you have already created.
self.myArray = [[NSMutableArray alloc] init];
This sends your class the following message:
- (void) myArray: (NSMutableArray *) myArray {
_myArray = myArray;
}
As you can, see the setter doesn't need any special initialization most of the time. The only time you want to create a custom setter is when you want to validate the incoming object has special properties. A contrived example is checking that the NSMutableArray is no larger than 10 objects:
- (void) myArray: (NSMutableArray *) myArray {
if (myArray.count < 10) {
_myArray = myArray;
}
}
Finally, I would like to point out that you can actually lazy instantiate objects using the short ternary operator and parenthetical return values. For example, the following statement:
- (NSMutableArray *) myArray {
return (_myArray = _myArray ?: #{}.mutableCopy);
}
Is equal to:
- (NSMutableArray *) myArray {
if(!_myArray) {
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
You can even macro this pattern into (WSM is my class prefix):
#define WSM_LAZY(object, assignment) (object = object ?: assignment)
So you can write statements like this:
- (NSMutableArray *) myArray {
return WSM_LAZY(_myArray, #{}.mutableCopy);
}
Or even use compound statement syntax to rewrite the original setter you presented as an example:
- (NSMutableArray *) myArray {
return WSM_LAZY(_myArray, ({
NSLog(#"created");
#{}.mutableCopy;
}));
}
I am working in Xcode 4.5.1 in Objective-C. I’m making a hearing test and want to store relevant data to each question in an array. I made a singleton MyManager. I use this to store data.
It is working fine for simple int/float values etc., but I’m stuck trying to use NSMutableArray. I’m new to Objective-C, so I’m assuming/hoping I've made some obvious mistake.
So, I want to fill mySNRArray with float values. I’ve come to understand that I can’t simply add floats, because it will only take objects. Thus, I use NSNumber.
Problem: When I try to read the data that I’ve supposedly added to the NSMutableArray, I get (null).
I will now provide the relevant code:
MyManager.h
#interface MyManager : NSObject
{
NSMutableArray *mySNRArray;
}
#property (readwrite) NSMutableArray *mySNRArray;
+ (id)sharedManager;
#end
MyManager.m
#implementation MyManager
#synthesize mySNRArray;
+ (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])
{
NSMutableArray *mySNRArray = [[NSMutableArray alloc]init];
}
return self;
}
#end
TestViewController.m
//First, I try to add a value to mySNRArray.
MyManager *Manager = [MyManager sharedManager];
NSNumber *tempStorage1 = [[NSNumber alloc] initWithFloat:mySNR];
[Manager.mySNRArray insertObject:tempStorage1 atIndex:questionNumber];
//The NSLog below is showing the correct value.
NSLog(#"%# : mySNR", tempStorage1);
...
for (n = 0; n < questionNumber; n++)
{
//Second, I try to acces the values I supposedly added to mySNRArray.
MyManager *Manager = [MyManager sharedManager];
//This NSLog below is showing (null).
NSLog(#"Value at %i in SNRArray = %#", n, [Manager.mySNRArray objectAtIndex:n]);
}
...
I hope somebody can explain my error.
change
NSMutableArray *mySNRArray = [[NSMutableArray alloc]init];
to
self->_mySNRArray = [[NSMutableArray alloc]init];
in your init method you are creating a local mutable array, but not assigning it to your property
Instead of creating a new object, use the ivar you created..in the init method.
_mySNRArray = [[NSMutableArray alloc]init];
Even you can ommit these, from your .h
{
NSMutableArray *mySNRArray;
}
+ (id)sharedManager
returns a value
static MyManager* sharedManager
Change the interface to
+ (MyManager*)sharedManager
and the compiler will tell you exactly what mistake you made.
Hi i want to implement my own Objects to manage my data, i was trying to make a two classes.
Class Continents that contains a Continent Objects
Here is my implementation:
#implementation OsContinents
#synthesize continentes;
-(id)init{
return [super init];
}
-(NSUInteger)count{
NSLog(#"%u",[continentes count]);
return [continentes count];
}
-(void)add:(OsContinent *)continente{
[continentes addObject:continente];
}
-(OsContinent *)getElementByIndex:(NSUInteger)index{
return [continentes objectAtIndex:index];
}
-(void)deleteContinentByIndex:(NSUInteger)index{
return [continentes removeObjectAtIndex:index];
}
-(void)deleteContinent:(OsContinent *)objContinent{
return [continentes removeObject:objContinent];
}
-(NSMutableArray *)getAll{
return continentes;
}
#end
Next i want to populate *continents Property with "Continent" Objects like this.
OsContinents *continentesCollection = [[OsContinents alloc] init];
for (NSString *strContinente in [data allKeys]) {
OsContinent *con = [[OsContinent alloc] init];
[con setContinente:strContinente];
NSLog(#"%#",[con getContinente]);
[continentesCollection add:con];
}
NSLog(#"%u",[continentesCollection count]);
But allways got ZERO in de count Method.
Note: NSLog(#"%#",[con getContinente]) print de data OK, the Continent Object is OK, the problem is in the "*continentes" inside the Continents Object-
Any Clue?
Your initializer does nothing but initialize the superclass. Use it to set up your own class:
- (id)init
{
self = [super init];
if (self)
{
_continentes = [[NSMutableArray alloc] init];
}
return self;
}
Otherwise, continentes will remain nil. Messaging nil is valid: methods simply don't do anything, and return 0.
If you want to completely hide the underlying mutable array (which is perfectly fine) then don't advertise it in your .h file as a property. Instead, at the beginning of your #implementation, declare a semi-private instance variable:
#implementation OsContinents
{
NSMutableArray *_continentes;
}
I say "semi-private" because you can always use the runtime engine to introspect objects. But it'll be hidden from normal use. If you ever subclass your object, you can always move the instance variable declaration from your #implementation to your #interface so that subclasses can get at it.
I come from a Java background and I am learning Objective C. I am trying to create a class that has a string array and a member function to modify the Array. My code looked like this:
#implementation TAWChapter
#synthesize mSubject;
#synthesize mItems;
- (id) init{
self.mItems = [[NSMutableArray alloc] init];
return self;
}
- (void) setSubject:(NSString *)subject{
self.mSubject = subject;
}
- (void) addItem:(NSString *)item{
[self.mItems addObject:#"asdf"];
}
#end
Which didn't work. I got a "[__NSArrayI addObject:]: unrecognized selector sent to instance " and a "NSInvalidArgumentException". After searching on internet, I changed the single line in the constructor to:
self.mItems = [self.mItems init];
It worked, but why? From a Java developer's point of view the first one makes more sense than the second one. And I have another line it's the same as the first one but it's working(not in a constructor). Can someone explain this to me please?
First of all, you should adhere to Objective-C coding conventions. In Objective-C, classes don't have constructors, they have initialisers. In Objective-C, initialisers invoke the initialiser of the superclass, so it should look like this:
- init
{
self = [super init];
if (!self) return nil;
// set up other stuff here
return self;
}
Second, unless you are using ARC, you might have a memory leak. The first line of your initialiser assigns an object that you own to a property that also likely takes ownership. You should use either:
// property takes care of ownership
self.mItems = [NSMutableArray array];
or:
// assign to instance variable directly with owned object
mItems = [[NSMutableArray alloc] init];
Apple sometimes discourage the use of accessor methods in initialisers because it can fiddle with things like KVO, but consistent use of accessor methods ensures proper object ownership and memory management.
By changing your line in your initialiser to:
self.mItems = [self.mItems init];
does nothing. When your initialiser method is called (which is typically just after it has been allocated), all instance variables are automatically set to nil. So what you are doing is just:
self.mItems = [nil init];
which is just:
self.mItems = nil;
and, don't use init without first allocating an instance, and never use init more than once.
If you do not let the superclass initialise itself, then it may manifest as problems in other areas. Do a Build & Analyze and ensure you have fixed up any issues pointed out by the analyser.
Since objective-c is a superset of c, it's basically c with some "OO" syntax sugar. Before you can create (or use!) an object, you must alloc space for it in the heap. you do this with [Class alloc]. The next step is the initialization of that space. alloc returns a pointer to that space in the heap, which you initialize with init ;)
So you call Class *myObjc = [[Class alloc] init];.
If you use inheritance (which you do since you inherit from NSOBject), you must make sure that your superclass initialized everything properly, thats the call to super. To make sure you don't get a runtime error, check for self != nil, which you do implicitly with if(self)
self.mItems = [self.mItems init]; // doesn't do anything, since you call the getter for mItems with self.mItems and try to init. then you try to set mItmes to itself.
use this code:
#implementation TAWChapter
#synthesize mSubject, mItems;
- (id)init
{
self = [super init];
if (self) {
mItems = [[NSMutableArray alloc] init];
}
return self;
}
- (void) setSubject:(NSString *)subject{
mSubject = subject;
}
- (void) addItem:(NSString *)item{
[mItems addObject:item];
}
#end
You should call super and assign its result to self in your init method:
- (id)init
{
self = [super init];
if (self) {
self.mItems = [[NSMutableArray alloc] init];
}
return self;
}
The another way could be creating NSMutableArray from NSArray:
NSMutableArray *myMutableArray = [NSMutableArray arrayWithArray:myArray];
I have a class Test which has an array of Foos. I want to provide access to the Foos without exposing the ivar directly. I'm trying to make this KVC compliant (also to pave the way for KVO compliance). I have:
Test.h
#interface Test : NSObject
{
NSMutableArray *foos;
}
#property (readonly, copy) NSMutableArray *foos;
#end
Test.m
- (id) init
{
self = [super init];
if (self != nil)
{
foos = [[NSMutableArray array] retain];
}
return self;
}
- (NSMutableArray*) foos
{
return [self mutableArrayValueForKey:#"foos"];
}
- (NSUInteger)countOfFoos
{
return [foos count];
}
- (id)objectInFoosAtIndex:(NSUInteger)index
{
return [foos objectAtIndex:index];
}
- (NSArray *)foosAtIndexes:(NSIndexSet *)indexes
{
return [foos objectsAtIndexes:indexes];
}
- (void)insertObject:(id)key inFoosAtIndex:(NSUInteger)index
{
[foos insertObject:key atIndex:index];
}
- (void)insertFoos:(NSArray *)foosArray atIndexes:(NSIndexSet *)indexes
{
[foos insertObjects:foosArray atIndexes:indexes];
}
- (void)removeObjectFromFoosAtIndex:(NSUInteger)index
{
[foos removeObjectAtIndex:index];
}
- (void)removeFoosAtIndexes:(NSIndexSet *)indexes
{
[foos removeObjectsAtIndexes:indexes];
}
This enters an infinite loop when a client tries to add a Foo:
Test *test = [[Test alloc] init];
NSMutableArray *foos = test.foos;
[foos addObject:#"adding object"]; // infinite loop here
What am I doing wrong?
- (NSMutableArray*) foos
{
return [self mutableArrayValueForKey:#"foos"];
}
An accessor should not use KVC to get the value of the property being accessed; the idea is that KVC goes through the accessors, because the accessors are closer to the value than KVC is.
The correct implementation of foos should return a copy, mutable or otherwise, of the array. Here's how I'd do it:
- (NSArray *) foos
{
return [[foos copy] autorelease];
}
I would also make all of the accessors public. Anything that wants to mutate the array or randomly access elements at specific indexes can do so that way. It's still safe and encapsulated because they're going through your accessors, not directly accessing the array.
There's not really any reason to use the KVC protocol methods yourself unless you don't know what key you'll access at the time you write the code. For example, if you were writing the nib loader or the Cocoa Bindings system, you would use KVC.
The problem is that the proxy NSMutableArray returned by mutableArrayValueForKey: first has to get the real array, which it does through the "foos" method. Since that's the one that returns a proxy NSMutableArray it enters an infinite loop. One solution is to use another name:
- (NSMutableArray*) mutableFoos
{
return [self mutableArrayValueForKey:#"foos"];
}
I spent a very long time on this problem and wanted to get this through an accessor. I wanted to clarify in the answer for those coming in. This is what I did:
#property (nonatomic,readonly,getter=getTheFoos) NSMutableArray* foos;
Then obviously implemented:
- (NSMutableArray*)getTheFoos {
return [self mutableArrayValueForKey:#"foos"];
}
Had to be careful though, getFoos appears to be an (undocumented) KVC accessor, because this sends it into the same loop.
Then onto KVO:
Test* test= [[Test alloc] init];
NSObject* obj= [[NSObject alloc] init];
NSMutableArray* arrTheData= test.foos;
[test.foos insertObject:obj atIndex:0];
[arrFoos insertObject:obj atIndex:0];
arrFoos can read the updated, mutated array (it will have two objects in it), but inserting into it will not fire KVO. Somewhere on my adventures, I saw that the return from mutableArrayValueForKey: doesn't return an NSMutableArray*, but a subclass of it, which might be the cause of it.