-2

I'm new to programming, and I feel a little intimidated posting, and I'm stuck. I don't want a quick fix! Please help me understand this.

I've created a custom method with a name, age, height and gender. It gets called when the NSMutableArray adds custom objects to the array. For some reason I cannot pull said items out of the NSMutableArray. Let's say the age needs to be printed out. I get a error saying...

-[Person componentsSeparatedByString:]: unrecognized selector sent to instance

Person.m

- (id)initWithName:(NSString *)n
            age:(int)a
         height:(float)h
         gender:(char)g
{
self = [super init];
if (self) {
    self.name = n;
    self.age = a;
    self.height = h;
    self.gender = g;
}

    return self;
}

- (NSString *)description
{
NSString *descriptionString = [[NSString alloc] initWithFormat:@"%@, %d, %.1f, %c",
                               self.name,
                               self.age,
                               self.height,
                               self.gender];
NSLog(@"Description String: %@", descriptionString);
    return descriptionString;
}

When adding objects to the NSMutableArray they get converted to a NSString? How do I get the peoples age without the whole strings name and height in the NSLog?

ViewController.m

[self.people addObject:[[Person alloc] initWithName:@"Jake" age:29 height:73.5 gender:'f']];
[self.people addObject:[[Person alloc] initWithName:@"Jerry" age:24 height:82.3 gender:'m']];
[self.people addObject:[[Person alloc] initWithName:@"Jessica" age:29 height:67.2 gender:'f']];

NSString *mystring1 = [self.people objectAtIndex:0];
NSLog(@"%@", mystring1);

// Works
//NSString *list = @"Norm, 42, 73.2, m";

NSArray *listItems = [self.people[0] componentsSeparatedByString:@", "];
NSLog(@"List Items: %@", listItems[1]);// age

Output

Description String: Jake, 29, 73.5, f
Jake, 29, 73.5, f
-[Person componentsSeparatedByString:]: unrecognized selector sent to instance

Solved:

Notice the extra [ age];

int age = [[self.people objectAtIndex:0]age];
NSLog(@"%d", age);
halfer
  • 19,824
  • 17
  • 99
  • 186
Jay
  • 49
  • 6
  • 1
    No, they don't get converted to a string when you add them to the array. What makes you think they do? – rdelmar Dec 27 '14 at 23:59
  • Where is `componentsSeparatedByString` documented?? In the doc for NSString. Is your Person class a subclass of NSString?? No. Do you know anything about object-oriented programming?? – Hot Licks Dec 28 '14 at 00:02
  • 1
    No need to feel intimidated... I think your code shows you at least have a basic understanding. You're just confused about how to access your object's properties. There's no need to convert your Person to a string, – Lyndsey Scott Dec 28 '14 at 00:15
  • The description tag made me think the array is getting smashed into a NSString. – Jay Dec 28 '14 at 15:14

5 Answers5

1

You can't perform componentsSeparatedByString on your Person object since it's not a string and doesn't have that method. (NSMutableArray objects are not automatically converted to NSStrings.) But to get the age of your Person, should be fairly simple anyway. Just access Person's age property:

int age = self.people[0].age;
NSLog(@"Age: %d", age);

Edit: You can technically use componentsSeparatedByString on your mystring1 NSString, like so:

NSArray *listItems = [mystring1 componentsSeparatedByString:@", "];
NSLog(@"List Items: %@", listItems[1]);// age

But again, I don't see the point of doing this when you can access the Person's age property directly.

Lyndsey Scott
  • 37,080
  • 10
  • 92
  • 128
1

The componentsSeparatedByString method is an instance method of NSString class. You can't call it on your Person class in this way.

I don't know why you need that code, if you are trying to access age, then:

NSLog(@"Age %d", self.people[0].age);

is enough. If you are trying to achieve any other thing, then you can get the components like:

NSArray *listItems = [self.people[0].description componentsSeparatedByString:@", "];
NSLog(@"List Items: %@", listItems[1]);// age
Midhun MP
  • 103,496
  • 31
  • 153
  • 200
1

I think the reason it seems like your object is getting converted to a string is this line,

NSString *mystring1 = [self.people objectAtIndex:0];

You need to specify the property if that is all you want to print

NSString *nameString = [[self.people objectAtIndex:0]name];
Christian Fox
  • 390
  • 2
  • 15
0

One would think this would work; however, I can not call .age on my array.

NSLog(@"Age %d", self.people[0].age);

Gives the following output...

Property 'age' not found on object of type 'id'

One would also think componentsSeparatedByString would work. It doesn't split my string by the comma and gives an error.

-[Person componentsSeparatedByString:]: unrecognized selector sent to instance

Solved READ UP

Jay
  • 49
  • 6
0

I think you are getting confused about why when you log the object you are getting a string, but you can't perform any methods on it.

And here is why you think you are getting a string, you've implemented the description property, which comes from the NSObject protocol. When you do a log of an object with a %@ parameter you are getting the result of this property, which is a string.

So looking at your code:

NSString *mystring1 = [self.people objectAtIndex:0]; //1
NSLog(@"%@", mystring1);                             //2

The first thing is that you are getting the first object out of your array. objectAtIndex returns an id, which is a pointer to an NSObject. It is not a strongly typed return. You are allocating it to an NSString which is wrong, because it as actually a person object. But since you aren't calling any NSString methods on it, the compiler is not flagging this incorrect assignment.

The second thing is that you are just getting the result of the description property, as I've already mentioned.

Your edited solution:

int age = [[self.people objectAtIndex:0]age];

Works, but here is why: you are getting the objectAtIndex:0 which returns an id. Then you are sending it the message age. Objective-C is a dynamic language, which means you can send any message to any object (although you get a run-time crash if the object does not implement the method) In this case, your Person object does implement an age method (since it's a public property) so you get an age back.

A different way of doing it:

NSInteger age;
Person *person = self.people.firstObject;
if (person) {
    age = person.age;
} else {
    age = NSNotFound;
}

Why am I doing it this way?

Firstly, I am getting the firstObject out of the array and putting it into a typed variable. This is safer, as if there is no object at index 0 (i.e. the array is empty) then you won't crash your app. firstObject returns the first object if it exists, on a nil if it doesn't.

Secondly, Only if the person has been correctly extracted from the array, do I assign the age to an NSInteger variable. You should prefer the specific types of NSInteger over int (and CGFloat over float) because they will use the correctly sized variable when running on 32-bit or 64-bit` systems.

Thirdly, if person cannot be created I am assigned the value of NSNotFound to the age. This is a typedef for a very large number, one that you can compare against. Just returning 0 in age is not enough to tell you there was an error. The person could have an age of 0. with this code you can test the age with:

if (age != NSNotFound) {
    // This is a valid age, do something with it
} else {
    // A person object could not be extracted from the array, this is not a valid age
}

Is this overkill? Not really. When you start writing real, complex apps, this sort of defensive programming will come naturally to you. Arrays can be empty, your data could be incorrectly created, etc. Writing code that gracefully handles these eventualities is the real skill of programming. Unfortunately, It's a bit more long winded, and most tutorials that you find on the web show you the simple, happy path.

I hope this gives you a better idea of what your code is doing and what more you could be doing to write robust code.

Abizern
  • 146,289
  • 39
  • 203
  • 257
  • Thank you for taking the time to write this novel of a response. It gave me many things to look over! – Jay Dec 28 '14 at 17:25