Can't add NSNumber object to NSMutableArray [duplicate] - ios

This question already has answers here:
Having trouble adding objects to NSMutableArray in Objective C
(2 answers)
Closed 6 years ago.
- (void)setToInitialStateMain
{
[super clearBoard];
if (_data[#"StoneOne"] != nil) {
NSDictionary* stoneOne = _data[#"StoneOne"];
NSNumber* c = stoneOne[#"Column"];
NSNumber* r = stoneOne[#"Row"];
NSInteger column = [c intValue];
NSInteger row = [r intValue];
[_boardCol addObject:[NSNumber numberWithInt:column]];
[_boardRow addObject:[NSNumber numberWithInt:row]];
}
}
So the #"StoneOne", #"Column", and #"Row" keys are coming from an NSDictionary plist file. When I try to convert the NSNumber #"Column" to NSInteger, everything works ok.
Now, the line [_boardCol addObject:[NSNumber numberWithInt:column]]; is ok in terms of 'column' being the correct integer (2). But, when setup a breakpoint at the end of the method call to examine _boardCol (as well as _boardRow), both NSMutableArray* instance variables are reading nil. Why is that?

All instance variables start out as nil. And a method call to nil returns nil (or does nothing). You need to instantiate the NSMutableArray yourself at some point. This is often done in -init, or inline code checking if the ivar is nil and if so allocating it:
if (self.boardCol == nil) {
self.boardCol = [[NSMutableArray alloc] init];
}
The above is not a safe thing to do if multiple threads could be involved. It's often easier to just create them in your -init method.

Related

Load UITableView sections by NSDate from NSDictionary?

I have an NSArray of NSDictionaries in my app. In each dictionary I hold an NSDate called "RunDate." The problem I am having now is that the code I am trying to do it with is very inefficient. Basically I only want one section per date out of all the dictionaries. Then in each section (sorted by that date), I would load the appropriate dictionary that had that date.
In the code below I made a new NSArray of NSDictionarys which held a date and number of that date (so I could know how many rows are in each section). The problem is, this code looks and feels very inefficient and I was wondering if there were any ways my code is incorrect or could be improved upon. There can be over 500 entries and the code I have now would be very slow. Does anyone have any suggestions on it?
runArray = [[NSMutableArray alloc] init];
runArray = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"RunArray"] mutableCopy];
runDictTableArray = [[NSMutableArray alloc] init];
for (NSDictionary *dict in runArray) {
NSDictionary *runInfoDict = [[NSMutableDictionary alloc] init];
NSDate *theDate = [dict objectForKey:#"RunDate"];
//Check if we already have this date in the saved run array
BOOL goToNextDict = FALSE;
for (NSDictionary *savedDict in runDictTableArray) {
if ([theDate compare:[savedDict objectForKey:#"RunDate"]] == NSOrderedSame) {
goToNextDict = TRUE;
break;
}
}
if (goToNextDict)
continue;
////////////////////////////
//Now we check how many more we have of this date
int numbOfDate = 1;
int index = (int)[runArray indexOfObject:dict];
for (int i = index; i < [runArray count]; i++) {
NSDictionary *dictInner = [runArray objectAtIndex:i];
if ([theDate compare:[dictInner objectForKey:#"RunDate"]] == NSOrderedSame) {
numbOfDate++;
}
}
////////////////////////////
[runInfoDict setValue:[NSNumber numberWithInt:numbOfDate] forKey:#"DateAmount"];
[runInfoDict setValue:theDate forKey:#"Date"];
[runDictTableArray addObject:runInfoDict];
}
Some suggestions:
You probably only need 1 NSMutableDictionary, rather than a NSMutableArray of NSDictionary. While looping through runArray, check if your dictionary has a value for your date (objectForKey returns a value). If it does, add 1 to the count. If it does not, add that date as a key to the dictionary with a value of 1. This way, you won't have to do the inner loop to get the number of times a date occurs. You won't need the 'go to next dictionary' logic either, I would think.
runArray = [[NSMutableArray alloc] init]; doesn't really do anything since you're immediately re-assigning runArray.
Consider using NSInteger over regular int, NSInteger will give you the appropriate size for whatever architecture your app is running on.
There's some cool syntax shortcuts you might like. You can avoid [runInfoDict setValue:[NSNumber numberWithInt:numbOfDate]... by simply writing [runInfoDict setValue:#(numbOfDate) ..., which will put the value into NSNumber for you.

assigning a value to an object's iVar inside an array [duplicate]

This question already has answers here:
Property '' not found on object of type 'id'
(2 answers)
Closed 6 years ago.
NSMutableArray *dataArray = [NSMutableArray array];
for (int i = 0; i<5; i++){
[dataArray addObject:[UITextView new]];
dataArray[i].text = "here";
^Property 'text' not found on object of type 'id'
}
I check the class, methods and iVars... everything is there, but I can't use them.
I want a UIScrollView with a user defined number of data fields (like 'Contants' app). So I load them into an array and put them onto the scroll view.
However, I can't call the methods. I want to use some kind of strut (array, dict, etc...)
I tried this:
textView1 = dataArray[i];
textView1.text = #"######### Here I am ##########";
But that doesn't seem to store in the object inside the array. I thought the array stored a pointer to the object and that textView1 would be a pointer to the object so it should store the value of '.text' to the same object.
What you tried seems a bit unclear to me, but this should work:
NSMutableArray *dataArray = [NSMutableArray array];
for (int i = 0; i<5; i++){
UITextView *t = [UITextView new];
[dataArray addObject:t];
t.text = #"here";
}

how to store an NSString variable in an NSMutable array in objective c [duplicate]

This question already has answers here:
Having trouble adding objects to NSMutableArray in Objective C
(2 answers)
Closed 7 years ago.
I am trying to store an NSString variable in an NSMutableArray:
#property(weak,nonatomic) NSMutableArray * stringLines;
//----
NSString * string=#"hello";
[self.stringLines addObject: string];
int i=0;
for (id obj in _stringLines){
NSLog(#"printing labels: index %x word %#",i, obj);
i++;
}
but when I try to print the mutable array, it shows me null values. I don't want to assign the text directly to the array, it has to be through the variable string.
Did you initialize self.stringLines? It may be nil. Try initializing to an empty array, then add the strings.
#property(weak,nonatomic) NSMutableArray * stringLines;
//----
self.stringLines = [NSMutableArray array];
NSString *string = #"hello";
[self.stringLines addObject:string];
NSLog(#"Print strings array: %#", self.stringLines);
That's because you have not allocated the array object. So if you add anything to a nil object you won't find anything inside it. Use this code
//----
self.stringLines = [[NSMutableArray alloc] init];
NSString * string=#"hello";
[self.stringLines addObject: string];
you can also initialize the array by adding the object with literal, so you need only 1 line of code instead of 3:
self.stringLines = #[#"hello"];

Mutating method sent to immutable object - iOS [duplicate]

This question already has answers here:
insert object in an NSMutableArray saved with NSUserDefaults
(6 answers)
Closed 8 years ago.
I have an NSMutableArray and I am trying to use the insertObjectAtIndex method, but it does not work. It seems to think I am trying to do something mutable with an immutable array..... but I am using an NS ..... MUTABLE.... Array. So what an earth could be wrong??
Here is my code:
if ([dataArray count] == 0) {
// Initialise the audio arrays.
dataArray = [[NSMutableArray alloc] init];
// Now add the data to the array.
[dataArray addObject:#"test 1"];
}
else {
[dataArray insertObject:#"test 2" atIndex:0];
}
NSLog(#"d in: %#", dataArray);
The dataArray is defined in the header file like so:
NSMutableArray *dataArray;
Thanks for your time, Dan.
Oh sorry everyone, I just figured out what was wrong. I did edit the code in my question a bit, but basically what I am actually populating the NSMutableArray with is an array stored in an NSUserDefault, (which of course returns immutable arrays only).
So even though I am using an NSMutableArray to store the data, its still has immutable contents. In order to over come this, I ended up using "mutableCopy" iOS method to get a mutable copy of the contents of the NSUserDefaults array.

Check if NSMutableArray contains an int

I am making an app which asks the user a series of questions. The question asked depends on the random int produced. When an int is used, I want to add it to an NSMutableArray, and then check if the array contains a number the next time a random number is chosen. I am currently using the following code to do this:
- (void) selectQuestionNumber {
textNum = lowerBounds + arc4random() % (upperBounds - lowerBounds);
if ([previousQuestions containsObject:[NSNumber numberWithInt:textNum]]) {
[self selectQuestionNumber];
NSLog(#"The same question number appeared!");
} else {
questionLabel.text = [self nextQuestion];
[self questionTitleChange];
NSLog(#"New question made");
}
[previousQuestions addObject:[NSNumber numberWithInt:textNum]];
}
However, the code NSLog(#"The same question number appeared!"); is never shown in the console, even when the same question will appear twice.
My code is obviously non-functional, so what code can I use to check if an NSMutable array contains an int?
Original solution (works with Array and Set):
-(void)selectQuestionNumber
{
textNum = lowerBounds + arc4random() % (upperBounds - lowerBounds);
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"intValue=%i",textNum];
NSArray *filteredArray = [previousQuestions filteredArrayUsingPredicate:predicate];
if ([filteredArray count]) {
[self selectQuestionNumber];
NSLog(#"The same question number appeared!");
} else {
questionLabel.text = [self nextQuestion];
[self questionTitleChange];
NSLog(#"New question made");
}
[previousQuestions addObject:[NSNumber numberWithInt:textNum]];
}
Best solution, and better performance, especialy with mutableSet ( According to Duncan C).
-(void)selectQuestionNumber
{
textNum = lowerBounds + arc4random() % (upperBounds - lowerBounds);
if ([previousQuestions containsObject:[NSNumber numberWithInteger:textNum]]) {
[self selectQuestionNumber];
NSLog(#"The same question number appeared!");
} else {
questionLabel.text = [self nextQuestion];
[self questionTitleChange];
NSLog(#"New question made");
// And add the new number to mutableSet of mutableArray.
[previousQuestions addObject:[NSNumber numberWithInteger:textNum]];
}
}
Your problem is likely something other than detecting membership of NSNumbers in an NSArray. It can take a large number of tries for a set of random numbers to repeat. It is theoretically possible for it to not repeat until every possible value has been generated once. For a large range of legal values it can take quite a while.
I suggest logging the values that you add to the array on each pass, and the new value.
Your code above always adds the new value to the array even it if matched, so your array is going to grow with duplicates. You would be better off only adding the new number to the array if it did not match. you would probably also be better off using an NSMutableSet instead of an array. NSSets contain at most one instance of an object, and their containsObject method is faster than that of NSArray.
Instead of using NSArray, you can use NSMutableIndexSet. This is the same as NSSet, with just NSUIntegers instead of objects. Very useful.
//during init
NSMutableIndexSet *tSet = [[NSMutableIndexSet alloc] init];
//...
//later in the code, in whatever loop you have on new values
NSUInteger newInt = lowerBounds + arc4random() % (upperBounds - lowerBounds);
if ([tSet containsIndex:newInt]){
//value already exists in the set
}
else {
//value does not exist, add it
[tSet addIndex:newInt];
}
NSMutableSet *myNumbers = [NSMutableSet Set]; // or NSMutableArray..
NSNumber *aNumber = [NSNumber numberWithInt:getRandomInt() ]; //let us say it returns 1.
[myNumbers addObject:aNumber];
-(BOOL)succesfullyAddNewUniqueRandomMember{
NSInteger randInt = getRandomInt(); //let us say it returns 1 again..
NSNumber *aSubsequentNumber = [NSNumber numberWithInt:randInt;
for (NSNumber *previousEntry in myNumbers){
if ([previousEntry isEqual:aSubsequentNumber]) return NO;
}
[myNumbers addObject:aSubsequentNumber];
return YES;
}
^ are these objects equal (aNumber, aSubsequentNumber) ? YES
^ are they the same object ? NO, two different NSNumbers made with equal integer..
NSSet will also happily add both, because they are not the same object.
therefore you need to loop through and compare directly to each previous member, the (already contains object) filter of NSSet will not do the trick.
by wrapping this in a -(BOOL) type method we can repeat it with a
while(![self succesfullyAddNewUniqueRandomMember])
In other words, in your code
if ([previousQuestions containsObject:[NSNumber numberWithInt:textNum]])
always returns NO because it is comparing NSNumber objects, not their integerValue.

Resources