Why no alloc init?

Discussion in 'Mac Programming' started by chrono1081, Dec 26, 2011.

  1. macrumors 604

    chrono1081

    Joined:
    Jan 26, 2008
    Location:
    Isla Nublar
    #1
    Hi guys,

    Did something change in Objective-C I am un-aware of?

    I was helping my friend work through the Big Nerd Ranch Objective-C book and look at this code:

    Code:
    #import <Foundation/Foundation.h>
    
    int main (int argc, const char * argv[])
    {
    
        @autoreleasepool {
            
            NSDate *now = [NSDate date]; //Why not NSDate *now = [[NSDate alloc] init]; ? Does this mean ARC is turned on?
            
            NSLog(@"The new date lives at %@", now);
            
        }
        return 0;
    }
    
    
    I go in an out of using Objective-C but I could have sworn you used to have to write something like this:

    NSDate *now = [[NSDate alloc] init];
    [NSDate date];

    I know the syntax for autoreleasepool is different now too (not sure why) but did something change here? Should I go out and update my Objective-C books?
     
  2. marc0015, Dec 26, 2011
    Last edited: Dec 26, 2011

    macrumors newbie

    Joined:
    Dec 12, 2011
    #2
    Date - class methods

    + (id)date

    Creates and returns a date initialised to the current date and time.

    Edit: the title should have read "NSDate - class methods"
     
  3. thread starter macrumors 604

    chrono1081

    Joined:
    Jan 26, 2008
    Location:
    Isla Nublar
    #3
    Ok I think I see what you mean.

    If its a class method I can call it like:

    NSDate *myDate = [NSDate date];

    but if I want to just create an instance of the class itself I can call:

    NSDate *myDate = [[NSDate alloc] init];

    Is that correct? (Sorry I am way more familiar with C++ than Objective-C).
     
  4. GorillaPaws, Dec 26, 2011
    Last edited: Dec 26, 2011

    macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #4
    You are correct. It's a common pattern to have factory methods that will return new autoreleased instances of a class initialized with some commonly desired behavior. In this case the +date class method of NSDate will create an autoreleased NSDate instance initialized to the current date/time.

    There's nothing wrong with:

    NSDate *myDate = [[NSDate alloc] init];

    except it's slightly more verbose. Also the former makes it clearer that you intend the current date. I had to look up the docs to see that the default behavior of init is also to return the current date. The former approach is slightly more declarative.

    EDIT: After re-reading your question, it seems you still might be confused about instances. Both approaches are creating instances of NSDate. The +alloc method is a class method that allocates memory for an instance of NSDate, and the -init method is an instance method that initializes the instance returned by +alloc to the current date. Using the class method +date will do the same thing, it will also return an instance of NSDate the only difference is that the instance it returns is not owned by you. As per the memory guidelines, with the first example, you would have to later release the object created with the init method if you weren't using ARC.

    Using NSNumbers might make the distinction more obvious:
    Code:
    NSNumber *firstNumber = [[NSNumber alloc] initWithDouble: 1.0];
    NSNumber *secondNumber = [NSNumber numberWithDouble: 1.0];
    
    if( [firstNumber isEqualToNumber: secondNumber] )
    {
        NSLog( @"This should print" );
    }
    
    [firstNumber release];  //  only if this code isn't ARC
    
    Both are doing the same thing, but secondNumber is not owned by you while firstNumber has a retain count of 1. In all other respects, they are identical.
     
  5. thread starter macrumors 604

    chrono1081

    Joined:
    Jan 26, 2008
    Location:
    Isla Nublar
    #5
    Thanks so much! It makes sense now. I'm still trying to get used to this ARC thing.

    The last time I used Objective-C significantly I had to worry about retain counts ;)

    I also have NO clue how I missed this in the documentation:

    "init
    Returns an NSDate object initialized to the current date and time.

    - (id)init"


    doh! :eek:
     
  6. macrumors 6502a

    Joined:
    Jan 15, 2007
    #6
    I would also add that not all factory methods actually create (i.e. call the static alloc method) a new object. For instance to get an instance of the current thread, you don't actually use

    and then set the thread to the current thread, instead you use:

    NSThread *bob=[NSThread currentThread]

    You have an NSThread instance, but nothing was added to the heap
     
  7. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #7
    Other examples would include class methods from NSUserDefaults, NSFileManager, NSWorkspace, NSFontPanel, etc, etc, which return a static object. I seem to recall that there are a handful of predefined NSNumber constants which do not respond to -release. And with this code
    Code:
    NSString *aString = [NSString stringWithString:@"wordswordswords"];
    I would not rely on the returned string being a different object from the string constant and may not respond to -release.
     
  8. macrumors 68040

    Joined:
    Apr 22, 2005
    #8
    They will still respond to release...but it just might not do anything.
     
  9. macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #9
    Sorry, poor wording on my part.
     
  10. thread starter macrumors 604

    chrono1081

    Joined:
    Jan 26, 2008
    Location:
    Isla Nublar
    #10
    Thank you so much guys for the extra tips!

    Unfortunately my mind is shot from working on a Maya plugin all day so I will have to try and make sense of everything tomorrow after sleep :eek:
     
  11. jiminaus, Dec 26, 2011
    Last edited: Dec 26, 2011

    macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #11
    I feel there's some terminology confusion happening here.

    NSDate's date is called a convenience constructor. A convenience constructor is a factory method, but not all factory methods are convenience constructors. Convenience constructors have names that begin with the name of class minus any prefix (or the name of a parent class in the case of inherited convenience constructors.

    Examples include string in NSString, stringWithFormat: in NSString, and string in NSMutableString. The last example is an example of an inherited convenience constructor.

    By convention, convenience constructors are notionally equivalent to an auto-released alloced/inited object. For example, [NSDate date] is notionally equivalent to [[[NSData alloc] init] autorelease].

    Convenience constructors typically return unique objects. But actually the convention is more that the client code should be able to treat the returned object as being unique. A convenience constructor can return a shared object if has no effect on the calling code. For example, [NSString string] could always returned the same empty string object. But it can only do that because NSString is immutable. [NSMutableString string] however would need to return a unique object each time it's called.

    In regards to NSString's stringWithString:, it could simply return the passed in object if and only if the passed in object is actually an NSString object. If the passed in object was actually an NSMutableString, then it would need to return an immutable copy of that passed in object. In fact, I would rely on this behaviour of NSString's stringWithString:. This way you can code defensively while only incurring the runtime and memory overhead when necessary.

    While [NSThread currentThread] could be called a factory method, it cannot be called a convenience constructor. So it is free to return a non-unique object.



    It would be wrong to call release on objects returned by [NSFileManager defaultManager] for example. It would be a violation of the memory-management rules. The behaviour of calling release on these objects cannot be relied upon. I would rely on it causing the program to crash, eventually.
     
  12. macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #12
    I thought they were synonyms. Thanks for sorting me out, and sorry for any confusion I may have caused.
     
  13. macrumors 68040

    Joined:
    Apr 22, 2005
    #13
    I wasn't saying anything about whether it is correct to call release on any particular object, only that the objects in question will respond to release, i.e. that they have a release method defined for them.
     
  14. macrumors 65816

    jiminaus

    Joined:
    Dec 16, 2010
    Location:
    Sydney
    #14
    Odd thing to say then. All objective-c objects will respond to release because it's declared in the NSObject protocol that all objective-c objects must conform to.
     
  15. Sydde, Dec 28, 2011
    Last edited: Dec 28, 2011

    macrumors 68000

    Sydde

    Joined:
    Aug 17, 2009
    #15
    Actually, IIRC, calling release on a singleton object would not result in an eventual crash. In Apple's guidelines for creating singletons, I think they recommend overriding -release in order to protect the singleton, but allow any others to be released (in case, for example, code were to attempt to create the object with alloc/init). It is still wrong to release a singleton, but probably not potentially disastrous.

    How they make this work correctly with ARC is something I have yet to investigate – presumably the compiler will make the correct inference for the static/self assignment in the -init method. EDIT: Silly me, obviously overriding +allocWithZone: prevents memory leaks from arising in the first place.
     

Share This Page