Initialize Array of Objects using NSArray - ios

I'm pretty new to Objective-C and iOS so I've been playing around with the Picker View. I've defined a Person Class so that when you create a new Person it automatically gives that person a name and age.
#import "Person.h"
#implementation Person
#synthesize personName, age;
-(id)init
{
self = [super init];
if(self)
{
personName = [self randomName];
age = [self randomAge];
}
return self;
}
-(NSString *) randomName
{
NSString* name;
NSArray* nameArr = [NSArray arrayWithObjects: #"Jill Valentine", #"Peter Griffin", #"Meg Griffin", #"Jack Lolwut",
#"Mike Roflcoptor", #"Cindy Woods", #"Jessica Windmill", #"Alexander The Great",
#"Sarah Peterson", #"Scott Scottland", #"Geoff Fanta", #"Amanda Pope", #"Michael Meyers",
#"Richard Biggus", #"Montey Python", #"Mike Wut", #"Fake Person", #"Chair",
nil];
NSUInteger randomIndex = arc4random() % [nameArr count];
name = [nameArr objectAtIndex: randomIndex];
return name;
}
-(NSInteger *) randomAge
{
//lowerBound + arc4random() % (upperBound - lowerBound);
NSInteger* num = (NSInteger*)(1 + arc4random() % (99 - 1));
return num;
}
#end
Now I want to make an array of Persons so I can throw a bunch into the picker, pick one Person and show their age. First though I need to make an array of Persons. How do I make an array of objects, initialize and allocate them?

There is also a shorthand of doing this:
NSArray *persons = #[person1, person2, person3];
It's equivalent to
NSArray *persons = [NSArray arrayWithObjects:person1, person2, person3, nil];
As iiFreeman said, you still need to do proper memory management if you're not using ARC.

NSMutableArray *persons = [NSMutableArray array];
for (int i = 0; i < myPersonsCount; i++) {
[persons addObject:[[Person alloc] init]];
}
NSArray *arrayOfPersons = [NSArray arrayWithArray:persons]; // if you want immutable array
also you can reach this without using NSMutableArray:
NSArray *persons = [NSArray array];
for (int i = 0; i < myPersonsCount; i++) {
persons = [persons arrayByAddingObject:[[Person alloc] init]];
}
One more thing - it's valid for ARC enabled environment, if you going to use it without ARC don't forget to add autoreleased objects into array!
[persons addObject:[[[Person alloc] init] autorelease];

No one commenting on the randomAge method?
This is so awfully wrong, it couldn't be any wronger.
NSInteger is a primitive type - it is most likely typedef'd as int or long.
In the randomAge method, you calculate a number from about 1 to 98.
Then you can cast that number to an NSNumber. You had to add a cast because the compiler gave you a warning that you didn't understand. That made the warning go away, but left you with an awful bug: That number was forced to be a pointer, so now you have a pointer to an integer somewhere in the first 100 bytes of memory.
If you access an NSInteger through the pointer, your program will crash. If you write through the pointer, your program will crash. If you put it into an array or dictionary, your program will crash.
Change it either to NSInteger or int, which is probably the best, or to NSNumber if you need an object for some reason. Then create the object by calling [NSNumber numberWithInteger:99] or whatever number you want.

This might not really answer the question, but just in case someone just need to quickly send a string value to a function that require a NSArray parameter.
NSArray *data = #[#"The String Value"];
if you need to send more than just 1 string value, you could also use
NSArray *data = #[#"The String Value", #"Second String", #"Third etc"];
then you can send it to the function like below
theFunction(data);

This way you can Create NSArray, NSMutableArray.
NSArray keys =[NSArray arrayWithObjects:#"key1",#"key2",#"key3",nil];
NSArray objects =[NSArray arrayWithObjects:#"value1",#"value2",#"value3",nil];

Related

IOS/Objective-C: Get value of property of object in array

I am trying to grab a relationship property of an object from a mutable array.
theNewItems[0].step is giving the error, Property 'step' found on object of type id.
Here is how I created the array:
NSMutableArray* theNewItems = [NSMutableArray arrayWithCapacity:20];
[theNewItems addObject:_itemsOnLoad[0]];
[theNewItems addObject:_itemsOnLoad[1]];
[theNewItems addObject:_itemsOnLoad[2]];
And here is how the array logs out
<Items: 0x1706842e0> (entity: Items; id: 0xd000000001900004 <x-coredata://AFF50577-0975-4124-AC70-074F355B73A0/Steps/p100> ; data: {
item = "0xd000000016000000 <x-coredata://AFF50577-0975-4124-AC70-074F355B73A0/Dare/p1408>";
sid = 545;
step = "step three";
wasdeleted = nil;
whenadded = nil;
}),
<Items: 0x170684330> (entity: Items; id: 0xd000000001840004 <x-coredata://AFF50577-0975-4124-AC70-074F355B73A0/Steps/p97> ; data: {
item = "0xd000000016000000 <x-coredata://AFF50577-0975-4124-AC70-074F355B73A0/Dare/p1408>";
sid = 544;
step = "step two";
wasdeleted = nil;
whenadded = nil;
}),
<Items: 0x170684380> (entity: Items; id: 0xd000000001780004 <x-coredata://AFF50577-0975-4124-AC70-074F355B73A0/Steps/p94> ; data: {
item = "0xd000000016000000 <x-coredata://AFF50577-0975-4124-AC70-074F355B73A0/Dare/p1408>";
sid = 543;
step = "step one";
wasdeleted = nil;
whenadded = nil;
})
)}
Should I be creating the mutablearray differently? Or how can I grab the property "step"?
Thanks in advance for any suggestions.
For clarification...
An NSArray (mutable or not) can hold objects of any type. So, when you "get" an object from the array, the compiler needs to know what you are getting.
Example:
NSMutableArray *a = [NSMutableArray arrayWithCapacity:40];
[a addObject:[UIView new]]; // add a UIView
[a addObject:#"A string"]; // add a NSString
[a addObject:#100]; // add a NSNumber
You now have an array with a View, a String and a Number. If you try to do this:
UIView *v = a[0];
NSString *s = a[1];
NSNumber *n = a[2];
You'll get warnings because while the types are correct, the compiler doesn't know that.
To actually use the objects you've stored in the array, you have to cast them. So, with the same example data:
UIView *v = (USView *)a[0];
NSString *s = (NSString *)a[1];
NSNumber *n = (NSNumber *)a[2];
is fine... you can use your v s and n objects as you'd expect.
For your specific object type of Items, you could:
Items *thisItem = (Items *)theNewItems[0];
NSString *theStep = thisItem.step;
or, more concisely:
NSString *theStep = ((Items *)theNewItems[0]).step;
In 2015, Apple introduced "Lightweight Generics" into Objective-C. This allows you to declare an array of type:
NSMutableArray <Items *> *theNewItems = [NSMutableArray arrayWithCapacity:20];
[theNewItems addObject:_itemsOnLoad[0]];
[theNewItems addObject:_itemsOnLoad[1]];
[theNewItems addObject:_itemsOnLoad[2]];
NSString *theStep = theNewItems[0].step;
And no more casting. Note that you still add your Items objects to the array in the same manner.
Another note: Reading around you'll find some debate about arrayWithCapacity. The most reliable info I've found explains that it perhaps used to make memory management a bit more efficient, but these days it's simply a "hint" and, really, only makes for readability as in:
"When I review my code, I see that I'm expecting this array to hold 40 objects."
It does not, however, pre-allocate memory... nor does it limit the array to 40 elements - the array will still expand as you continue to add objects to it.
You don't need to use arrayWithCapacity you can just make an array using [[NSMutableArray alloc] init]; which will have no limit on capacity.
To get the property just say ((Items *)theNewItems[x]).step, x being the index at which you want the property. Also if you want to skip the casting step when pulling the object out of the array define your array as NSMutableArray<Items *> * theNewItems = [[NSMutablearray alloc] init] then you can just say theNewItems[x].step

replacing an NSDecimalNumbers with another in NSArray crashes

I have a NSMutabelArray and I want to do some additions inside of it. I do this by calling a functions with then create a subarray with the items where the calculations have to be done on.
- (NSDecimalNumber *)calculate:(NSMutableArray *)arrayToCalculate {
while ([arrayToCalculate containsObject:(#"+")]) {
NSUInteger signeLocation = [arrayToCalculate indexOfObject:(#"+")];
[arrayToCalculate replaceObjectAtIndex:(signeLocation-1)
withObject:([[arrayToCalculate objectAtIndex:(signeLocation-1)]
decimalNumberByAdding:[arrayToCalculate objectAtIndex:(signeLocation+1)]])];
[arrayToCalculate removeObjectsAtIndexes:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange((signeLocation), 2)]];
}
return [arrayToCalculate lastObject];
}
I initialised the arrayToCalculate by:
NSMutableArray *subArray =
[inputArray subarrayWithRange:(rangeOfCalculationItems)];
Every time I run this code it crashes. I am pretty sure it is bc I used subarray on an NSMutableArray and initialised it as NSMutableArray even when the message gives me back a NSArray, but I don't know how I could fix it or it is even the problem.
I copied your method and tested it like this:
NSArray *items = #[
[[NSDecimalNumber alloc] initWithString:#"1"],
#"+",
[[NSDecimalNumber alloc] initWithString:#"2"]
];
NSLog(#"%g", [self calculate: [items mutableCopy]].floatValue);
The code works and the printed result was 3. Your issue must be somewhere else. Are you sure your array is in fact mutable? Note [items mutableCopy].

Return an array of NSNumbers with NSIntegers as parameters

Return an array of NSNumbers with NSIntegers as parameters
I am trying to create an array of NSNumbers between two integers, inclusively.
Two parameters are provided number and otherNumber
Note: either number or otherNumber may be the lower number, but the string always includes numbers from lowest to highest.
I want to be able to return an array of NSNumber between two integers, inclusively.
I am not getting any compiling errors the program runs, however, it's not passing the unit tests. Am I missing something? Your help is greatly appreciated!
- (NSArray *) arrayOfNumbersBetweenNumber:(NSInteger)number andOtherNumber: (NSInteger)otherNumber {
NSNumber *newNumber = [NSNumber numberWithInteger:number];
NSNumber *otherNewNumber = [NSNumber numberWithInteger:otherNumber];
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:newNumber, otherNewNumber, nil];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES];
[mutableArray sortUsingDescriptors:#[sortDescriptor]];
NSArray *myArray = [mutableArray copy];
return myArray;
Objective-C
- (NSArray *)arrayOfNumbersBetweenNumber:(NSInteger)number andOtherNumber:(NSInteger)otherNumber {
NSMutableArray *mutableRange = [NSMutableArray new];
for (NSInteger i = number; i <= otherNumber; i++) {
[mutableRange addObject:#(i)];
}
return [mutableRange copy];
}
You are only adding two numbers, your two bounds, to the array, not all of the intermediary numbers. Also adding numbers and then sorting is pretty inefficient. You can use a simple for loop.
Here is an answer in Swift because a Playground made it easier and I have to leave you some work :)
func arrayOfNumbersBetween(startNumber:NSInteger, endNumber:NSInteger) -> NSArray {
let loopStart=min(startNumber,endNumber)
let loopEnd=max(startNumber, endNumber)
let returnArray=NSMutableArray()
for number in loopStart...loopEnd {
returnArray.addObject(NSNumber(integer: number))
}
return returnArray
}

Returning an NSMutableArray in objective C

I have been trying to work out how to return an MSMutableArray in objective c, I have this code here.
- (NSMutableArray*)generateRandomNumber{
NSMutableArray *unqArray=[[NSMutableArray alloc]init];
int randNum;
int counter = 0;
while (counter< 6) {
randNum = arc4random_uniform(40.0);
if (![unqArray containsObject:[NSNumber numberWithInt:randNum]]) {
[unqArray addObject:[NSNumber numberWithInt:randNum]];
counter++;
}
}
return unqArray;
}
- (IBAction)button:(id)sender {
NSMutableArray *results = [[NSMutableArray alloc]init];
*results = generateRandomNumber();
}
This is my code at the moment, where it says,
NSMutableArray *results = [[NSMutableArray alloc]init];
I get the following errors...
Implicit declaration of function 'generateRandomNumber' is invalid in C99
Assigning to 'NSMutableArray' from incompatible type 'int'
If anybody is willing to show me my mistake and help me out as well as many others I will appreciate it as much as possible.
Thanks to all who help me out!!!
generateRandomNumber is a method, not a C function, so use [self generateRandomNumber] to call it, and you are assigning results incorrectly, so:
results = [self generateRandomNumber];
Alternatively if you want to define it as a C function, use:
NSMutableArray *generateRandomNumber() {
...
}
Also, as pointed-out by #Larme, there is no need to allocate results before assigning it from generateRandomNumber.
- (IBAction)button:(id)sender {
NSMutableArray *results = [[NSMutableArray alloc]init];
results = [self generateRandomNumber];
}
Or more simply:
- (IBAction)button:(id)sender {
NSMutableArray *results = [self generateRandomNumber];
// Do something with this Array
}
Though you should consider a different name for that method as currently it sounds like it is returning one number, not an array of random numbers.
When calling/using a function in Objective-C
it's [self generateRandomNumber]
When you declare a variables it's NSMutableArray *results = [[NSMutableArray alloc]init]; and use if like results = mutableArry, * before it is not needed..
about your problem, you better do it like this:
- (IBAction)button:(id)sender
{
NSMutableArray *results = [self generateRandomNumber];
}
you dont need to allocate anymore, because you are passing an mutable array that is already allocated..
Your method generateRandomNumbers should work as expect the problem is here:
- (IBAction)button:(id)sender {
NSMutableArray *results = [[NSMutableArray alloc]init];
*results = generateRandomNumber();
}
The first thing is that you are creating an unneeded mutable array, second your are trying to substitute the value of the pointer that points to result array, third generateRandonNumber is a method not a function you should call it like [self generateRandomNumber].
Also I would implement a optimization since I'm pretty sure that you are not going to modify the random number array, the returned instance should be an immutable copy.
Here the final code:
- (NSArray*)generateRandomNumber{
NSMutableArray *unqArray=[[NSMutableArray alloc]init];
int randNum;
int counter = 0;
while (counter< 6) {
randNum = arc4random_uniform(40.0);
if (![unqArray containsObject:[NSNumber numberWithInt:randNum]]) {
[unqArray addObject:[NSNumber numberWithInt:randNum]];
counter++;
}
}
return unqArray.copy;
}
- (IBAction)button:(id)sender {
NSArray *results = nil;
results = [self generateRandomNumber];
}
Your generateRandomNumbers method should work with no problem, but in the IBAction there you try to put an INT directly into the array, you need to wrap it in an NSNumber like you have in the other method there
Your method is correct.
What you are doing wrong is here: *results = generateRandomNumber();
There is no need of an * here,because in this way you are trying to assign the pointer address of your array to results object.
Secondly you are trying to call an Objective-C method in C syntax.
So the correct syntax would be: results = [self generateRandomNumber];

How to add objects named obj1, obj2, obj3, ... to NSMutableArray in for loop

Is there better way for
_studentArray = [[NSMutableArray alloc] initWithObjects:student0, student1, student2, student3, student4, student5, student6, student7, student8, student9, student10, student11, student12, student13, student14, student15, student16, student17, student18, student19, nil];
using FOR loop?
As the others have pointed out, it's probably best to not create 20 different objects in uniquely named variables (student0, student1, student2, etc) in the first place. Better to create them in an array in the first place, as others have pointed out.
If you MUST deal with lots of instance variables like that, you should be able to use valueForKey to find them all:
_studentArray = [NSMutableArray arrayWithCapacity: 20];
for (int i=0; i< 20; i++)
{
NSString *keyName = [NSString stringWithFormat: #"student%d", i];
id aStudentVar = [self objectForKey: keyName];
_studentArray[i] = aStudentVar;
}
I haven't tested this, but it should work. Again, though, having that many sequentially named instance variables is a bad "code smell". There is almost certainly a better way to do whatever it is you're doing.
Depends on the source from where and at a time how many objects you are getting
if you are getting all the object in a go you can add as above or by using a for loop to add individual objects.
If you are getting a single object at a time add it and add other objects as well.
It depends where the student objects came from, how they were created etc. But I think you should re-think your code to NOT have a reference for each student like you did. Just think about how hard would it be if facebook had done the same as you did to refere to each user?
If you are creating the student objects by yourself, try an approach like this:
- (void)viewDidLoad {
_studentArray = [NSMutableArray new];
for (int i = 0; i < 20; i++) {
Student *student = [[Student alloc] init];
[_studentArray addObject:student];
}
//At this point you will have the same that you did.
//To access the 2nd student for example, you could do
Student *theSecond = _studentArray[1]; //(because array index starts from 0)
theSecond.name = #"Peter";
}
or if you want to create and add to _studentsArray at the same time:
- (void)viewDidLoad {
_studentArray = [NSMutableArray new];
[self registerStudentWithName:#"Peter"];
[self registerStudentWithName:#"John"];
//and so on...
Student *firstStudent = _studentArray[0];
NSLog(#"%#", firstStudent.name); //Prints Peter
}
- (void)registerStudentWithName:(NSString *)name {
Student *student = [[Student alloc] init];
student.name = name;
[_studentArray addObject:student];
}
Its very simple, all you have to do is start for loop for some count.
for(int i=0;i<20;i++) {
NSString *obj = [[NSString alloc]init];
[_studentArray addObject:obj];
}

Resources