Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

larswik

macrumors 68000
Original poster
Sep 8, 2006
1,552
11
First time trying to use this popUpButton. I read through some documentation but I can't seem to figure out the code I need to add to give it a value. I use this code to populate the pull-down with a list of things. I have a variable set up so if they select d100 it should assign the integer value of 100 to the variable? I know it is in front of my eyes in the documentation but I can't find it.


Code:
-(void)awakeFromNib
{
    [sidedDiceSelection addItemWithTitle:@"d100"];
    [sidedDiceSelection addItemWithTitle:@"d20"];
    [sidedDiceSelection addItemWithTitle:@"d12"];
    [sidedDiceSelection addItemWithTitle:@"d10"];
    [sidedDiceSelection addItemWithTitle:@"d8"];
    [sidedDiceSelection addItemWithTitle:@"d6"];
    [sidedDiceSelection addItemWithTitle:@"d4"];
    [sidedDiceSelection addItemWithTitle:@"d2 "];
}

I was thinking I could do something like this
Code:
[sidedDiceSelection addItemWithTitle:@"d100" setObjectValue:100];
But I im getting an error saying "Return type defaults to id". I am setting the "d100" and it works fine. Is it because I am trying to assign an integer to an object?
 
There is no addItemWithTitle:setObjectValue: in NSPopUpButton, that why you're getting the compiler warning.

What you might want to do is use the representedObject property of NSMenuItem. Basically an NSPopUpButton is button that menu triggered will show an NSMenu. An NSMenu is made up of NSMenuItem objects.

So you could create the NSPopUp menu with something like this. (Entered directly, untested).
Code:
-(void)awakeFromNib
{
    NSArray *sides =
      [NSArray arrayWithObjects:
          [NSNumber numberWithInt:100],
          [NSNumber numberWithInt:20],
          [NSNumber numberWithInt:10],
          [NSNumber numberWithInt:8],
          [NSNumber numberWithInt:6],
          [NSNumber numberWithInt:4],
          [NSNumber numberWithInt:2],
          nil
      ];

    for (NSNumber *side in sides) {
       // Append an item to the popup's menu
       NSString *title = [NSString stringWithFormat:@"d%@", side];
       [sidedDiceSelection addItemWithTitle:title];
       // Get the newly appended menu item
       NSMenuItem *menuItem = [sidedDiceSelection lastItem];
       // Set the representedObject property so the menu item
       // carries the number of sides
       [menuItem setRepresentedObject:side];
    }
}

Then in an action, you get the number of sides for the selection with something like:
Code:
NSMenuItem *selectedMenuItem = [sidedDiceSelection selectedItem];
NSNumber *sides = [selectedMenuItem representedObject];
 
I'm back on my Mac now (ahhh). I've found a complication.

I thought the NSPopUpButton's action would trigger when a menu item was selected. Unfortunately it doesn't. It seems we are required to setup a connect between each menu item and the controller.

Assuming the IBOutlet is called selectDice:, then setup code becomes.
Code:
- (void)awakeFromNib
{
    NSArray *sides =
    [NSArray arrayWithObjects:
     [NSNumber numberWithInt:100],
     [NSNumber numberWithInt:20],
     [NSNumber numberWithInt:10],
     [NSNumber numberWithInt:8],
     [NSNumber numberWithInt:6],
     [NSNumber numberWithInt:4],
     [NSNumber numberWithInt:2],
     nil
     ];
    
    for (NSNumber *side in sides) {
        // Append an item to the popup's menu
        NSString *title = [NSString stringWithFormat:@"d%@", side];
        [sidedDiceSelection addItemWithTitle:title];
        // Get the newly appended menu item
        NSMenuItem *menuItem = [sidedDiceSelection lastItem];
        // Set the representedObject property so the menu item
        // carries the number of sides
        [menuItem setRepresentedObject:side];
        [COLOR=blue]// Connect menu item
        [menuItem setAction:@selector(selectDice:)];
        [menuItem setTarget:self];[/COLOR]
    }
}

BTW This the programming equivalent of drawing a line between an object and outlet in IB.
 
It seems overly complicated to set these up with values for a NSPopUpButton :)

The array that you set up, is that just an array of integers that seems separate from the pull down?

It would seem smart to me (Still learning of course) that an NSPopUpButton would utilize a mutable dictionary scheme. Object being the value stored and key being the the printed type on the pull down that you select. I learned about dictionary's the other day :)

-Lars
 
The array that you set up, is that just an array of integers that seems separate from the pull down?

The array just drives the loop. If you're not getting that, here's another way.

Code:
- (void)addSidedDiceSelection:(int)numberOfSides
{
  NSNumber *side = [NSNumber numberWithInt:numberOfSides];
  // Append an item to the popup's menu
  NSString *title = [NSString stringWithFormat:@"d%@", side];
  [sidedDiceSelection addItemWithTitle:title];
  // Get the newly appended menu item
  NSMenuItem *menuItem = [sidedDiceSelection lastItem];
  // Set the representedObject property so the menu item
  // carries the number of sides
  [menuItem setRepresentedObject:side];
  // Connect menu item
  [menuItem setAction:@selector(selectDice:)];
  [menuItem setTarget:self];
}

- (void)awakeFromNib
{
  [self addSidedDiceSelection:100];
  [self addSidedDiceSelection:20];
  [self addSidedDiceSelection:10];
  [self addSidedDiceSelection:8];
  [self addSidedDiceSelection:6];
  [self addSidedDiceSelection:4];
  [self addSidedDiceSelection:2];
}
 
The last version you showed me was for some reason easier to understand. I can see what is happening now. Thanks again for your help.
 
While I am waiting for my oil change and on my iPad I had an Ahh-hah moment. The developer library is kind of confusing if you don't know what you are doing or looking for. Also looking for a terminology check up.

Jim wrote this line : NSNumber *side = [NSNumber numberWithInt:numberOfSides];

'NSNumber' is a class. '*side' is the pointer variable pointing to the new (object or instance object?) set up in memory. ’=' my assignment operator. Now the next one 'NSNumber' shows up again, is this just written again to show the path to the (class methods) that a class has? Now the next line 'numberWithInt' is a class method which is listed in the developer help under that class. ':' this is an (assignment operator?) that allows the class method to take the argument which is 'numberOfSides'.

Now, these 2 methods 'numberWithInt' and 'initWithInt'. These 2 methods seem to do the same thing but 1 is a class method and the other is an
instance method.

If my code is to build an object with that value
Code:
NSNumber *side = [NSNumber numberWithInt:numberOfSides];
And later I want to change that value I would do something like this
Code:
[side intValue: 5];
Would this be accessing the same value that was set by 'numberWithInt'.

Thanks.
 
Now the next one 'NSNumber' shows up again, is this just written again to show the path to the (class methods) that a class has?
It's written again to name the exact class that numberWithInt will be sent to. If you don't name the exact class, you can't send a message to it. If you name a different class, then that class will be sent the message.

':' this is an (assignment operator?) that allows the class method to take the argument which is 'numberOfSides'.
The : is part of the method name. It definitely IS NOT an assignment operator. It's not an operator at all. It marks the point in the method where a parameter must be placed.


Now, these 2 methods 'numberWithInt' and 'initWithInt'. These 2 methods seem to do the same thing but 1 is a class method and the other is an
instance method.
They do similar things. They are not the same.

You already noted one is a class method, the other an instance method. Given that difference, they can't be the same. Also, one returns an object you own, the other returns an object you don't own. Go back and read the Memory Management Guide if necessary.


If my code is to build an object with that value
Code:
NSNumber *side = [NSNumber numberWithInt:numberOfSides];
And later I want to change that value I would do something like this
Code:
[side intValue: 5];
Would this be accessing the same value that was set by 'numberWithInt'.
First, an instance of NSNumber is immutable. You can't change its value. Having an immutable class is a recurring design pattern in Cocoa: NSArray, NSDictionary, NSString, and others are all immutable. There is often a mutable version of the base class (e.g. NSMutableArray, NSMutableString, etc.). Not so for NSNumber.

Second, there is no method named intValue: (note I've included the colon in the name. There is a method named intValue (no colon), but it takes no parameters. You can't just throw in colons wherever you want. There must be an actual method with identical name and colons. That's one of the things wrong with the first post in this thread. You just randomly stuck together method-name parts and colons:
Code:
addItemWithTitle:@"d100" setObjectValue:100
There is no method whose name is addItemWithTitle:setObjectValue: so it fails.

Go to the NSNumber class reference doc:
http://developer.apple.com/library/...asses/NSNumber_Class/Reference/Reference.html

Look in the lefthand column. At the bottom there is a Companion Guides heading. The first item is "Number and Value Programming Topics". Read that.

You should also read the class reference doc for NSValue, which is the superclass of NSNumber. There's a link for it on the NSNumber class reference doc page, at the top, labeled "Inherits from".
 
Thanks Chown33,

It's written again to name the exact class that numberWithInt will be sent to
I can see that. So 'NSNumber *side' is the first step and it's just saying " I want the pointer variable 'side' to point to an object created from the class NSNumber. The next part '= [NSNumber numberWithInt:numberOfSides];' is saying, "and by the way, when you create the object I want it initialized with these settings as default".

You just randomly stuck together method-name parts and colons:
haha, yep, that's a noob approach keep tweaking it till it works :)

So what I am pretty much taking away from your explanation is that once an object is created, it's created with values already initialized to be used but not changed because they default to immutable.

I noticed that I never need to alloc or init classes that are not mine, this must be class methods built into the classes. So I would never write code like this
Code:
NSNumber *myNumber = [NSNumber alloc]init];
Because once I did I created myNumber which is immutable. That is why I always initialize immutable objects, with values, when they are created, yes?

Thanks again Chown33. I will read the memory management again too

-Lars
 
I can see that. So 'NSNumber *side' is the first step and it's just saying " I want the pointer variable 'side' to point to an object created from the class NSNumber.
Mostly right.

It's saying "I declare a variable named 'side'. Its type is 'pointer to NSNumber'." That may seem the same as what you wrote, but it's subtly different.

Notice that I didn't say "created from the class NSNumber". That's because there are subclasses of NSNumber, and it is perfectly valid (and sometimes intentional) to declare the variable's type as the superclass, but then create the instance using a subclass. Here's a contrived example:
Code:
NSNumber *side = [NSDecimalNumber decimalNumberWithString:@"2.71"];
I say "contrived" because I have no real reason for doing this here, it's just to show what's possible.

The next part '= [NSNumber numberWithInt:numberOfSides];' is saying, "and by the way, when you create the object I want it initialized with these settings as default".
It's not really "and by the way". It's a very specific and compulsory request. It's not optional in any way.

So what I am pretty much taking away from your explanation is that once an object is created, it's created with values already initialized to be used but not changed because they default to immutable.
Neither classes nor objects "default to immutable". That's only true here because this particular class represents immutable objects. It's never because this is accidental or optional. It's always because the class itself is intentionally designed to represent immutable objects.

I noticed that I never need to alloc or init classes that are not mine, this must be class methods built into the classes.
It's not built into classes. The classes are designed and implemented that way. You can write class methods in your classes, if you want them and intend to use them.

So I would never write code like this
Code:
NSNumber *myNumber = [NSNumber alloc]init];
Because once I did I created myNumber which is immutable. That is why I always initialize immutable objects, with values, when they are created, yes?
Well first, I have to fix the missing [ :
Code:
NSNumber *myNumber = [[NSNumber alloc]init];

Now, from a designer's point of view, can you think of a reason to have an NSNumber that contains no number?

Some classes it makes sense that you might want an empty representation, such as an empty NSArray or NSDictionary. Others it might be debatable. But does it ever make sense to have an "empty number" object? I'm not even sure what such a thing would represent. It's not zero, because 0 is clearly a number.

Second, from a designer's point of view, if you can't think of a reason to have an "empty" instance of a class, then you would not have a plain -init method. As a designer and implementor, this is an intentional design decision, because it's what makes sense for the way the class and it's instances are intended to be used.

If you want to make a design argument that an "empty" NSNumber is the NaN value, then maybe you allow a plain -init. But NSNumber's designers clearly didn't do that, so there isn't a plain -init method.

Third, there isn't a plain -init method in NSNumber, so you can't use this code [[NSNumber alloc]init] even if you wanted to.
 
Cool, it seems that when you answer my questions I am let with more questions from your responses :)

I can see why an empty object is useless for the most part. I think for the most part I understand what I am doing, I am just getting my syntax messed describing it to you, but it does make a difference.

So when I use my iPhone contacts list and what to add a new contact I hit the '+'. I then enter the name and number and hit done. I hope I explain this right: hitting the '+' does not initialize a new object. It prompts me to fill in the information (for a class method) and then when I hit 'done' an object is created with the contact information to access. This object would have to be mutable because if he gets a new, or additional number I need to modify that object, right?

Before you started mentioning this I thought that hitting the '+' created (there is that word again, created) a new object. Then the information was added to it after it was created, but that was wrong way of thinking of it.

Thanks again for taking the time Chown33!!!!

-Lars

-Lars
 
Third, there isn't a plain -init method in NSNumber, so you can't use this code [[NSNumber alloc]init] even if you wanted to.

There always is an init method, because NSObject provides one and you can't remove a method. What NSNumber does in the face of a plain init I can't say because I can't test it right now.

In my own classes, when I don't want parameter-less initialization, I override init to log a message and return nil.
 
There always is an init method, because NSObject provides one and you can't remove a method.
You're right; I temporarily forgot that.

What NSNumber does in the face of a plain init I can't say because I can't test it right now.

In my own classes, when I don't want parameter-less initialization, I override init to log a message and return nil.

Code:
#import <Foundation/Foundation.h>

int main(int arcgc, char *argv[])
{
	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	NSNumber *foo = [[NSNumber alloc] init];

	NSLog( @"foo: %@", foo );
	
	[pool drain];
	return 0;
}
Output:
Code:
2011-06-27 16:38:50.320 a.out[9624] foo: (null)
Personally, I wouldn't do it this way. Using the plain -init is clearly a serious programmer error. The Cocoa convention is that serious programmer errors throw exceptions. There may be some mitigating reason, but I don't have an "NSNumber Design Rationale" document, so I don't know.
 
Personally, I wouldn't do it this way. Using the plain -init is clearly a serious programmer error. The Cocoa convention is that serious programmer errors throw exceptions. There may be some mitigating reason, but I don't have an "NSNumber Design Rationale" document, so I don't know.

One can only guess, but here is a possible rationale.

The -init method of any class should never throw an exception. If any real value were assigned, say 0, to the NSNumber, then the program would continue with unexpected results.

By leaving the value null, an exception might be thrown as soon as you tried to use it where a real number was required.

Only a guess...
 
One can only guess, but here is a possible rationale.

The -init method of any class should never throw an exception. If any real value were assigned, say 0, to the NSNumber, then the program would continue with unexpected results.

By leaving the value null, an exception might be thrown as soon as you tried to use it where a real number was required.

Only a guess...

I too have an aversion to throwing an exception during initialization. But I can't come up with a clear justification for that aversion in Objective-C. Perhaps it's a C++ thing I've carried over to Objective-C.
 
By leaving the value null, an exception might be thrown as soon as you tried to use it where a real number was required.
It won't. In Objective-C, nil is a valid message receiver. So sending messages like intValue will return 0.

A recent thread on cocoa-dev list: -(id)init methods, NSExceptions, and returning nil.

I have seen init methods that contain NSAssert()'s, which will throw an exception if the assertion fails.
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.