How to have user create class instance

Discussion in 'iOS Programming' started by Abrexas, Mar 25, 2013.

  1. Abrexas macrumors member

    Joined:
    Jul 29, 2011
    #1
    Hey guys,
    I really feel like I am drawing a blank over something very obvious, but I've been stuck for some time.

    Say I have a class:
    Code:
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    {
         NSString *name;
         NSInteger *age;
         NSInteger *weight;
    }
    @end
    
    With appropriate getters and setters. Now hardcoding a "person" is hella simple. But I need the user to be able to create new instances of the person class. I feel like this would be easier in java since I have a better understanding of constructors in java.
    How I assume it would go, is that I could have the user fill in various text fields of the desired input, and then on button click fill a constructor with said data. But I don't see where I will [alloc] the data.

    By just using the getters and setter I can do this easily, as long as I have already hard coded the initiation of the class.
    IE:
    Code:
    Person *Mike = [[Person alloc]init];
    
    And then filling the rest via button click and such.

    Can someone tell me how to let the user create the whole thing?
    Thanks
     
  2. dejo, Mar 25, 2013
    Last edited: Mar 25, 2013

    dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    I would make an init method that takes values for your three instance variables. As in: initWithName:age:weight:
     
  3. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #3
    You would allocate and init the new instance inside of the method that gets called by the button press. I'd suggest then adding it to a collection like an NSMutableArray or something so you can refer to it elsewhere. IE:

    Code:
    (IBAction) createClicked:(UIButton *)button {
        Person *p = [[Person alloc] init];
        p.name = nameField.text;
        // Other properties get set here...
        [peopleArray addObject:p];
    }
    dejo's suggestion to use an init method that takes initial values is also a good one, as that is, after all, the purpose of an init method...

    Quick aside: is it actually necessary to call an init after every alloc? It seems to me that alloc aught to initialize the allocated memory to some sort of blank state and that calling an init with no arguments aught to be a waste of a call? Does the compiler get rid of this waste of a call for you, or am I mistaken in thinking it's a waste?
     
  4. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #4

    Objective C uses 2 step object creation. Alloc simply allocates the memory for the object, but does NO initialization. Allocating an object but not initializing it leaves it in an undefined state. Do not do that.

    To the OP, alloc/init is the equivalent of constructors in Java or C++.

    As the other poster suggested, you simply need to add logic in your button action that creates a new object, saves it somewhere like an array, and presents it to the user. The details depend on what these objects mean to the user. The details depend on what the objects are and how you represent them in your UI.

    A user interface that manages a table view and has a "+" (add) button creates new model objects in response to the user clicking the "+" button, adds those objects to the data model, and then updates the table view to display the new model contents.
     
  5. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #5
    Yes, it is.

    It's not a waste. Initializing the allocated memory is init's job; hence, the name. alloc is pretty much the same regardless of the class; whereas init can vary depending on the class of instance it is working with. This two-step approach also allows you to define a number of init "vectors" for your class, such as UIView's initWithFrame: and initWithCoder:.
     
  6. Abrexas thread starter macrumors member

    Joined:
    Jul 29, 2011
    #6
    Thank you all for the feedback. My problem with doing it in the button pressed method is that every time I click the button it would create a new Person *p.
    I am storing multiple people into an array, and when I tried doing this it didn't work. Even if the first button press and the second button press had different information like name and such, since the class variable name was always p, the array would just fill multiple times with the latest declaration of p. Am I doing something wrong or do I need to go about it in a different way?
     
  7. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #7
    That's what you want, right? Press button to create a new person => new Person created.

    Without seeing your code, we can't tell if you're doing something wrong. Show us the entire button press method, as well as any code that adds your Person to your array.
     
  8. Abrexas thread starter macrumors member

    Joined:
    Jul 29, 2011
    #8
    I didn't take the time to test it with a button. I did something more on the lines of
    Code:
    Person *p = [[Person alloc]init];
    p.firstName = @"Steve";
    
    NSMutableArray *testArray = [[NSMutableAray alloc] init];
    [testArray addObject:p];
    
    p.fiestName = @"Greg";
    
    [testArray addObject:p];
    
    NSLog(@"@%",testArray);
    
    And the result of that being {Greg, Greg}

    Note: this isn't actual code. My program uses a lot more variables, but this is a simplified version of the problem I am having.
     
  9. Duncan C, Mar 26, 2013
    Last edited: Mar 27, 2013

    Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #9
    Look at what your code does. You create a person object and set it's first name to "Steve". Then you create an array and add that object.

    Then you change the first name of that person object to "Greg" and add it again. You now have 2 pointers to the same person object in your array, and you've replaced the first name with "Greg". So your array now contains 2 pointers to the same object, with a first name of Greg.

    Your code should look like this instead:


    Code:
    NSMutableArray *testArray = [NSMutableArray arrayWithCapacity: 4];
    
    Person *personA, *personB, *personC, *personD;
    
    personA = [[Person alloc]init];
    personA.firstName = @"Bob";
    [testArray addObject: personA];
    
    personB = [[Person alloc]init];
    personB.firstName = @"Carol";
    [testArray addObject: personB];
    
    personC = [[Person alloc]init];
    personC.firstName = @"Ted";
    [testArray addObject: personC];
    
    personD = [[Person alloc]init];
    personD.firstName = @"Alice";
    [testArray addObject: personD];
    
    NSLog(@"@%",testArray);
    
    (Code cleaned up to use all 4 temp variables personA - personD)

    Note that you should add a method description to your person object that looks something like this:

    Code:
    - (NSString *) description;
    {
       NSString *result = [NSString stringWithFormat: @"first name = %@, last name = %@, ssn = %@", 
       self.firstName, self.lastName, self.ssn];
    }
    
    If you do that, logging an array of person objects will invoke the description method of each Person in the array.

    (Note that you'll have to change the code above to log the real properties in your Person object. I used firstName, lastName, and ssn as an example.)


    In the first block of code above I used 4 different variables, personA through PersonD, for clarity. I could just have easily used the same variable, aPerson, and overridden it with a new object after adding each object to the array:


    Code:
    Person *aPerson;
    
    aPerson = [[Person alloc]init];
    aPerson.firstName = @"Bob";
    [testArray addObject: aPerson];
    
    //Replace the old person object with a newly allocated one:
    aPerson = [[Person alloc]init];
    aPerson.firstName = @"Bob";
    [testArray addObject: aPerson];
    
    NSLog(@"@%",testArray);
    
     
  10. Abrexas thread starter macrumors member

    Joined:
    Jul 29, 2011
    #10
    Exactly what I meant by drawing a blank about something obvious haha. Thank you!
    Unfortunately I am doing homework on my windows partition so I cannot go check my code out. I know that I am later comparing things to my arrow of object, and having multiple objects may pose a problem when doing so. However I might have made my code to compare the contents of the objects int he array, int hat case your method would work fine.
    If there a way that you know of to dynamically create a new instance of the class with an original name?

    Either way, thank you very much for the input!
     
  11. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #11
    How about something like?:
    Code:
    aPerson = [[Person alloc] initWithName:nameField.text];
    (You'll have to create the initWithName: method.)
     
  12. Abrexas thread starter macrumors member

    Joined:
    Jul 29, 2011
    #12
    With doing that it seems I would still be adding "aPerson" to the array. Or is there a way to make a method that changes the instances name?

    I got around to testing the previous method and it works flawlessly with my code. So thank you all! Still curious for learnings sake on the whole original name thing, but the problem is solved!

    Thanks!!
    - Steven French
     
  13. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #13
    I'm not sure I understand. Why do you want to change the instance's name?
     
  14. Abrexas thread starter macrumors member

    Joined:
    Jul 29, 2011
    #14
    I no longer need to. I thought I did before because I had completely overlooked re-allocating before. Now I am just curious to see if it is possible.
     
  15. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #15
    You have a conceptual misunderstanding. A local variable or instance variable for an object is a pointer to an object of that type. You can store any object of that type there. That variable is not a unique ID of an object that always points to the same object

    Code:
    Person *aPerson;
    
    aPerson = [[Person alloc] init]; //Creates a person object 
    
    //Do something with with aPerson 
    aPerson = [[Person alloc] init]; //Creates a NEW person object 
    
    Don't think of the variable name as the "original name" of the object. It's not. Think of a variable as a slip of paper with somebody's phone number written on it (in pencil). Erase the number and write a different number on it. It's still the same piece of paper (variable) but not it points to a different person.
     
  16. Abrexas thread starter macrumors member

    Joined:
    Jul 29, 2011
    #16
    Yes I understand this now. That was the obvious detail that I was asking over before ^_^
    I had just been using in my code, id's that mean something about the class so that it was easier for me to see what was going on. As I mentioned, I understand how to do what needs to be done now because of your posts. I was just curious still for no reason.

    Again, thanks for the help! ^_^
     
  17. iphonedude2008, Mar 27, 2013
    Last edited by a moderator: Mar 28, 2013

    iphonedude2008 macrumors 65816

    iphonedude2008

    Joined:
    Nov 7, 2009
    Location:
    Irvine, CA
    #17
    If it is a direct subclass of nsobject then yes it is a waste of time. Nsobject's init method looks like this

    Code:
    - (id) init 
    {
    return self;
    }
    so it is not doing anything.
    Other wise you should call init
     
  18. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #18
    Sure, that may be, but it is a good habit to get into anyways. And besides, perhaps the compiler is able to detect this situation and handle accordingly to avoid unnecessary methods call.
     
  19. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #19
    Hold on a minute. NSObject includes private instance variables like retainCount that must be initialized. The set of NSObject methods and NSObject protocol methods is pretty rich. There may be other private instance variables we are unaware of, even for NSObject.

    I bet even NSObject's init method does more than return self. It bet it zeros out other memory, and might even do other housekeeping for the runtime.

    If your object is a direct SUBCLASS of NSObject, then YOUR instance variables need to be zeroed out.

    In any case, your contract with Objective C says that you should initialize every object after allocating it. Failure to do so may result in undefined behavior.
     
  20. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #20
    Even if that were the current behavior, it's a private implementation and so subject to change between SDK editions (or wherever NSObject is implemented.)
     
  21. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #21
    The +alloc method is guaranteed to return a zeroed object. Check the reference docs.


    This is the correct reason. I believe it still holds.
     
  22. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #22

    Well what do you know. You're right. Thanks for pointing that out.

    The simple fact is, Objective C uses a 2 part object creation, and bad things may happen if you don't initialize an object.

    Worse, it might appear to work today, and in some future release of the OS, or on older versions, or different hardware, it might not work.
     

Share This Page