Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective C: Class Method Explanation

As a part of my transition process from C++ to Objective-C, I intensively read book Cocoa and Objective C Up and Running.

In one of the book code examples, there is a line that does not make sense to me with my current level of knowledge:

It is a declaration of class method + (Photo*) photo;.

Could anybody explain me the reason, please, why the author had decided for the method (Photo*) photo; to declare it as a class method instead of instance method?

I have studiet the theory, that the instane method is something like a class member function and that class method is something like static function in C++. But this still does not answer my question.

Here is the declaration code:

#import <Foundation/Foundation.h>


@interface Photo : NSObject{

    NSString* caption;
    NSString* photographer;    
}

+ (Photo*) photo;

- (NSString*) caption;
- (NSString*) photographer;

- (void) setCaption: (NSString*)input;
- (void) setPhotographer: (NSString*)input;

@end

The implementation code follows:

#import "Photo.h"


@implementation Photo

- (id)init
{
    self = [super init];
    if (self) {
        [self setCaption:@"Default Caption"];
        [self setPhotographer:@"Default Photographer"];
    }

    return self;
}


+ (Photo*) photo {
    Photo* newPhoto = [[Photo alloc] init];
    return [newPhoto autorelease];
}


- (NSString*) caption {
    return caption;
}


- (NSString*) photographer {
    return photographer;
}


- (void) setCaption:(NSString *)input {
    [caption autorelease];
    caption = [input retain];
}


- (void) setPhotographer: (NSString *)input {
    [photographer autorelease];
    photographer = [input retain];
}


- (void)dealloc
{
    [self setCaption:nil];
    [self setPhotographer:nil];

    [super dealloc];
}

@end
like image 651
Bunkai.Satori Avatar asked Dec 04 '22 21:12

Bunkai.Satori


2 Answers

The + (Photo*) photo method is a Factory Method that encapsulates the details of creating an object of the Photo class.

A Factory Method enforces encapsulation, and allows an object to be requested without inextricable coupling to the act of creation.

In this particular example the information being hidden by the factory method is memory management, since the client does not need to worry about releasing the returned object.

It is a common practice in Objective-C APIs to provide factory methods in classes that return autoreleased objects of that same classes. These methods must not contain any of the words “alloc”, “new”, “copy”, or “mutableCopy”, which, according to the convention, indicates that the caller doesn't own the returned object, i.e. it doesn't have to be explicitly released.

Related resources:

  • Memory Management Rules
like image 168
Enrico Campidoglio Avatar answered Jan 14 '23 17:01

Enrico Campidoglio


Meta answer:

One issue; that method should be declared as returning id and should return [[[self alloc] init] autorelease]; (one line or two, doesn't matter -- just should refer to the Class directly). As it is, Photo is gonna be a pain to subclass.

Expanding -- given this:

+ (Photo*) photo {
    Photo* newPhoto = [[Photo alloc] init];
    return [newPhoto autorelease];
}

If the class were subclassed, this factory method would not work without being overridden to do pretty much the same thing. However, since Objective-C doesn't support co-variance and contra-variance, there would be no way to declare the subclass's implementation of +photo to return an instance of the subclass without also running a significant risk of compiler warnings. Alternatively, you could down-cast the return value to the more specific class, but that is rife with fragility.

Instead, do this:

+ (id) photo {
    id newPhoto = [[self alloc] init];
    return [newPhoto autorelease];
}

This fixes both issues:

  • since it uses self, it'll instantiate an instance of whatever class it is implemented on, including subclasses of Photo.

  • since it returns id, callers can do both of the following without issue:

    Photo *p = [Photo photo]; SubclassOfPhoto *s = [SubclassOfPhoto photo];

like image 38
bbum Avatar answered Jan 14 '23 16:01

bbum