NSArray only changed if class is passed - objective-c

I have a class with a NSArray property using ARC with nothing fancy...
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
property(nonatomic, strong) (NSArray *) myArray;
#end
#import "MyClass.h"
#implementation MyClass
#synthesize myArray = _myArray;
#end
If an attempt is made to use a method inside of another class to try to set that array it does not set it if only the array is passed; however, it does set if the class is passed, I'm not sure why that is....
The code that attempts this both ways is below...
MyClass *myClass = [[MyClass alloc] init];
[self setArrayByPassingArray:myClass.myArray];
NSLog (#"%#", myClass.myArray)
//result is null
[self setArrayByPassingClass:myClass];
NSLog (#"%#", myClass.myArray)
//result is test, test2...
-(void)setArrayByPassingArray:(NSArray *)arrayToSet {
arrrayToSet = [[NSArray alloc] initWithObjects: #"test", #"test2", nil];
}
-(void)setArrayByPassingClass:(MyClass *)classWithArrayToSet {
classWithArrayToSet.myArray = = [[NSArray alloc] initWithObjects: #"test", #"test2", nil];
}
I tried some other methods with just strings and the strings are not changed, so I'm not sure why they are changed if class containing them is passed...

Your setArrayByPassingArray: method in the first example assigns the newly created NSArray to its parameter, which is passed by value, and is promptly discarded upon exiting from the method. What happens here is that a copy of the reference to myArray is made (not a copy of the array, only a copy of a reference to that array) before calling setArrayByPassingArray:. That copy is no longer attached to the myArray member of MyClass.
Your second example is not passing a class - it's passing an instance of the class, and then it correctly uses the dot notation to assign the myArray property in your instance. That's why this second example works, and the first example does not.
If you use NSMutableArray instead of NSArray throughout your program, you can rewrite your first example to make it work:
-(void)setArrayByPassingArray:(NSMutableArray *)arrayToSet {
[arrrayToSet setArray:[[NSArray alloc] initWithObjects: #"test", #"test2", nil]];
}

Related

Initializer element is not a compile time constant

Using objective-c to write a program. I'm getting an error (initializer element is not a compile-time constant) and am not sure I follow why it's occurring. I'm just trying to initialize an array. I'm also using xcode6. My questions are: how can I rewrite this correctly in Objective-c and what would it look like in the new Swift? Also why is there an error - I don't follow how to implement some of the other threads on this question?
Name.h
#import <Foundation/Foundation.h>
#interface Name : NSObject
#property (nonatomic, retain) NSMutableArray *myArray;
#end
Name.m
#import "Name.h"
#implementation Name
NSMutableArray *myArray = [[NSMutableArray alloc] init]; //error shows up here - initializer element is not a compile-time constant
[myArray addObject:#"Object 1"];
[myArray addObject:#"Object 2"];
[myArray addObject:#"Object 3"];
#end
You should init the variable only inside a method
try override
-(id) init
{
self = [super init];
if(self)
{
myArray = [[NSMutableArray alloc] init];
}
return self;
}
Error
As the error say, you can only initialize compile time constant in the implementation of your class
This will work:
NSString* abcd = #"test";
Because #"test" is a constant and will never change after the compilation of your code.
[[NSMutableArray alloc] init] is not a constant and this is why you got an error. You will have to implement an init method to initialize your array.
Swift
For the swift part of your question:
You can still use NSArray in swift or use the swift Array type.
You can check out the Working with Cocoa Data Types Documentation or the Apple collections types Swift Documentation.
If you still want to use NSArray in swift :
var array:NSMutableArray = NSMutableArray()
array.addObject("test1")
array.addObject("test2")
// or
array:NSMutableArray = ["test1", "test2"]
Or if you want to use the swift array :
var array:String[] = ["test1", "test2"]
// or
var array:String[] = String[]()
array.append("test1")
array.append("test2")
Try to edit you "Building Settings"->"Compile Sources As" to "Objective-C++", maybe can solve your problem.

Accessing NSDictionary in Singleton Class w/o Creating Copies

I have a singleton as follows, which creates an instance of NSDictionary to hold my data. Here is the .h:
#interface FirstLast : NSObject
#property (strong, nonatomic, readonly) NSArray *firstArray;
#property (strong, nonatomic, readonly) NSArray *lastArray;
#property (strong, nonatomic, readonly) NSDictionary *fl;
+ (FirstLast *) firstLast;
- (NSDictionary *) tempDic;
#end
Here is the .m
#implementation FirstLast
#synthesize firstArray = _firstArray;
#synthesize lastArray = _lastArray;
#synthesize fl = _fl;
+ (FirstLast *)firstLast {
static FirstLast *singleton;
static dispatch_once_t once;
dispatch_once(&once, ^{
singleton = [[FirstLast alloc] init];
NSLog(#"FirstLast instantiated");
});
return singleton;
}
- (NSDictionary *) tempDic{
_firstArray = [[NSArray alloc] initWithObjects:#"Bob", #"Joe", #"Sally", #"Sue", nil];
_lastArray = [[NSArray alloc] initWithObjects:#"Jones", #"Johnson", #"Thompson", #"Miller", nil];
_fl = [NSDictionary dictionaryWithObjects:_firstArray
forKeys:_lastArray];
NSLog(#"tempDic just made _fl at this address");
NSLog(#"%p", _fl);
return _fl;
}
#end
All of this works fine. In the view controller I instantiate all this for the first time (works fine too):
NSLog(#"VC is setting up tempDic");
[[WordsClues wordsClues] tempDic];
When I try to gain access to tempDic elsewhere, like this:
NSInteger rIndex = arc4random_uniform(4) + 1;
NSString *fname = [[[FirstLast firstLast].tempDic allValues] objectAtIndex:rIndex];
it works fine, but, when I repeat this process, each time I'm creating a new tempDic. I know this because the NSLog giving the address gives a different answer each time. I really want to access the existing dictionary, which is what I thought my singleton was going to accomplish. Clearly I'm either not accessing tempDic correctly or I misunderstand what the singleton can do for me or I have the tempDic set up wrong. The goal is to get a random value from a single copy of tempDic and not write local copies of tempDic all over the place. Thanks.
Why do you recreate the dictionary in -tempDic at all?
I.e. move the dictionary instantiation code to init and then just return _fl; in tempDic.
No worries -- we've all been there [new].
In your FirstLast class, implement the init method as something like:
- init
{
self = [super init];
if ( self ) {
_fl = ... create your dictionary here ...;
}
return self;
}
Then change -tempDic to:
- (NSDictionary*)tempDic {
return _fl;
}
I would highly recommend that you read a good intro to Objective-C book. I'm a purist and, thus, would recommend going to the source for the information, but there are lots of books available.
The questions you are asking are more in line with "What is object oriented programming and how does Objective-C work?".
To answer your question; FirstLast is a class and the singleton pattern makes sure there is exactly one instance of that class. By moving the creation of the dictionary to the init method -- which is called only once and who stores a reference to the created dictionary in an instance variable -- you avoid creating multiple dictionary instances.
Every time you call tempDic, you create a new copy of it. What you should do is add you code for creating the dictionary to your alloc instance, and then just retrieve it in your getter.
Alternativly you can do this
- (NSDictionary *) tempDic{
if( _fl == nil )
{
_firstArray = [[NSArray alloc] initWithObjects:#"Bob", #"Joe", #"Sally", #"Sue", nil];
_lastArray = [[NSArray alloc] initWithObjects:#"Jones", #"Johnson", #"Thompson", #"Miller", nil];
_fl = [NSDictionary dictionaryWithObjects:_firstArray
forKeys:_lastArray];
NSLog(#"tempDic just made _fl at this address");
NSLog(#"%p", _fl);
}
return _fl;
}

Problems using NSMutableArray

I have created an object of type NSMutableArray
#import <Foundation/Foundation.h>
#interface MyCustomObject : NSMutableArray
{
}
#end
in one of my classes, I delcare an instance:
MyCustomObject *myObj = [[NSMutableArray alloc] init];
But xcode is giving a warning on this line:
Incompatible pointer types initializing 'MyCustomObject *' with an expression of type 'NSMutableArray *'
Any idea what is wrong? It works fine, but just wondering why it is throwing a warning and how to resolve it?
You can assign a child class to a variable typed as its super-class, but you cannot assign a super-class to a child class variable.
So, you'd want to:
MyCustomObject *myObj = [[MyCustomObject alloc] init];
Firstly, as everyone else has said, you need to instantiate your class if you want an instance of your class. Evidently the reason you're not doing this is because you tried instantiating your class and it didn't work. That's the deeper problem: You can't simply subclass NSArray or NSMutableArray. As noted in the documentation, NSArray is a class cluster, which is basically a short way of saying "NSArray doesn't actually implement most of its methods."
In order to subclass NSArray, you essentially have to provide all of its functionality yourself. It is generally much easier to either create a category on NSArray or create a custom class that has an array as a member.
I think you've got things backwards. MyCustomObject is a NSMutableArray, but NSMutableArray isn't a MyCustomObject. Your variable myObj should be a NSMutableArray if it needs to be able to hold both NSMutableArrays and MyCustomObjects.
You should be instantiating the object as:
MyCustomObject *myObj = [[MyCustomObject alloc] init];
Otherwise, all you're doing is making an NSMutableArray and don't need your custom object (since none of its functionality would work.)
It should be:
MyCustomObject *myObj = [[MyCustomObject alloc] init];
if you do:
NSMutableArray *myObj = [[MyCustomObject alloc] init];
It will create an istance of MyCustomObject, but you will have a NSMutableArray pointer, so your new instance will just be seen as a NSMutableArray.
In this case:
MyCustomObject *myObj = [[NSMutableArray alloc] init];
It is an error. In previows a MyCustomObject can ben seen as a simple NSMutableArray. but a NSMutableArray cannot ever be seen as a MyCustomObject. It is a simple inheritance property.
Maybe this can help
http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming
Try allocating a MyCustomObject instead:
MyCustomObject *myObj = [[MyCustomObject alloc] init];

iOS - NSMutableArray shows objects out of bounds on setting property

I have implemented the following code to assign NSMutableArray to a property -
NSMutableArray * anArray = [responseDictionary valueForKeyPath:#"tags"];
NSLog(#"The array length is=%d",[anArray count]);
for (NSString *s in anArray) {
NSLog(#"you are %#", s);
}
[self setActiveTagArray:anArray];
It prints out the string values fine. But in the setter function, if I place a breakpoint I see that it shows there are two objects but they are "Out of Scope". What does this mean? What am I doing wrong? My getter also does not fetch any values. The property functions -
-(void)setActiveTagArray:(NSMutableArray *)tags
{
activeTagArray = [[NSMutableArray alloc] init];
activeTagArray = tags;
//NSLog(#"%#",[activeTagArray count]);
}
-(NSMutableArray *)getActiveTagArray
{
return activeTagArray;
}
Is activeTagArray a class variable as well as a property. Consider using _activeTagArray as the class variable name. And then in the .m file just use #synthesize activeTagArray = _activeTagArray;, and for get the second two methods completely.
Response to comment:
You said "I have implemented the following code to assign NSMutableArray to a property". I took this to mean you have "#property (nonatomic, retain) NSMutableArray *activeTagArray;" in your .h file. If this is the case then you would access it thru otherObject'sNameForYourClassHere.activeTagArray.
#synthesize create accessors & mutators for you.

Question about release the object

I have a class which contain a NSArray object.
like this
#import <Foundation/Foundation.h>
#interface myClass : NSObject {
NSArray *myArray;
}
#property (nonatomic, retain) NSArray *myArray;
#end
In .m file, I init myArray in init method,and release myArray in dealloc method.
in a method, I create its object and add it to a NSMutableArray.
myClass *my = [[myClass alloc] init];
NSLog(#"init finish %d",[my retainCount]);
NSMutableArray *a = [[NSMutableArray alloc] init];
[a addObject:my];
NSLog(#"array added finish %d",[my retainCount]);
NSLog(#"array added finish %d",[my.myArray retainCount]);
[my release];
When i add object "my" to NSMutableArray, the retainCount of "my" was added.
but myArray wasn't. Did it mean that I must retain myArray by my self?
or something other I can do.
Can this code work normal after I release "my" object?
[a objectAtIndex:0];
Thanks!
Another great example of exactly why you should...
NEVER call -retainCount!
retainCount is useless, misleading and a waste of your time.
In this case, the reason why the retain count happens to be zero "unexpectedly" is because my.myArray returns nil. That happens because you never assign the created mutable array to myArray.
You need something like this (Class name capitalized to follow convention):
MyClass *my = [[MyClass alloc] init];
NSMutableArray *a = [[NSMutableArray alloc] init];
my.myArray = a;
[a addObject:my];
Note that this creates a retain cycle between my and the array. I.e. you will need to remove my from the array manually (or remove the array from my manually) whenever you release myArray and, of course, you can't do that in dealloc because dealloc will never be called until my has been removed from myArray.
I am not sure if that is the full source code above but for my.myArray to be retained you have to actually assign something to it.
So in your above example you created an NSMutableArray:
NSMutableArray *a = [[NSMutableArray alloc] init];
And then you added your class to it:
[a addObject:my];
But no where have you created an array and assigned it to myClass as in:
NSArray *anArray = [[NSArray alloc] init.....];
my.myArray = anArray;
At that point, myArray will get a reference to an object and will retain it (since you specified retain in your myArray prop declaration).
Perhaps if you clarified what it is you are trying to do or posted some more full source code?