1

Possible Duplicate:
Why doesn’t Objective-C support private methods?
Private Method Declaration Objective-C

What I would like to do is make the implementation of a particular method in Objective C private (to use a Java term). That is, a method that the class can call as needed without any worry that the method will be overridden by a subclass. Perhaps the term is "safe from being overridden."

As an example, an object has multiple init methods, and there's a private initialize method to do the common initializations:

- (void) initWithFile:(NSString *)file
{
    if ((self = [super init]) != nil) {
        self.file = file;
        self.url = nil;
        [self initialize]
    }
    return self;
}

- (void) initWithURL:(NSURL *)url
{
    if ((self = [super init]) != nil) {
        self.url = url;
        self.file = nil;
        [self initialize]
    }
    return self;
}

- (void) initialize
{
    ...
}

This works, and if initialize isn't declared in the .h file, it's "protected" from being called by other classes.

I get into problem with subclasses (assuming the following class is a subclass of the previous example):

- (void) initWithFile:(NSString *)file
{
    if ((self = [super initWithFile:file]) != nil) {
        [self initialize];   // my local initializations
    }
    return self;
}

- (void) initialize
{
    ... // my private stuff
}

The problem is that in this example, the base class's initialize method is now never called, and the subclass's initialize method is called twice, since the subclass's initialize method is overriding the base class method.

I know I could call [super initialize] in the subclass, and omit the call in the subclass init method, but that requires the subclass to know the internals of the base class. Since the subclass shouldn't need access to the source of the base class, and should only need the .h interface file, it has every reason to think that it can implement an initialize method without causing the unwanted side-effect of changing the behavior of the base class.

I'm asking this because I had an issue that was directly involved with properties not being set in an initialize function, only to find out it was being usurped by a subclass, and it took me some time to debug this. I'd like to prevent this from happening in the first place.

Community
  • 1
  • 1
lar3ry
  • 510
  • 4
  • 8
  • 2
    Explanation for why here: http://stackoverflow.com/questions/2158660/why-doesnt-objective-c-support-private-methods – bbum Dec 22 '12 at 18:34

6 Answers6

2

There is no such thing in Objective-C. What you can do though is simply not defining the method.

Apple does this often like this.

@interface SomeClass ()
- (void)_someNonPublicMethod;
@end

This is a category, which simply says what should be defined in the class. You would do this in your .m file.

Like some other answers already mentioned, you can find out which methods a class implements using class-dump, and you can then override those, even if they are private.

Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
IluTov
  • 6,807
  • 6
  • 41
  • 103
  • one _ in name is reserved for apple IIRC, so this isnt good advice IMO – Daij-Djan Dec 22 '12 at 19:07
  • @Daij-Djan I've seen this in a bunch of 3rd party code, but I guess you're right, so he could just use `- (void)myPrefix_someMethod;` – IluTov Dec 22 '12 at 19:21
  • 1
    @Daij-Djan That's a myth. Who told you you couldn't use an underscore in method names (I've seen methods triple underscored before). The compiler really doesn't give a crap. +1 to balance. – CodaFi Dec 22 '12 at 21:33
  • of course you CAN technically use them but you run into the danger of overriding private API.
    The name is kept at runtime btw. this is of course not a compiler issue at all, it is merely one of convention!
    – Daij-Djan Dec 23 '12 at 08:21
  • @Daij-Djan I guess you are technically correct, that it's cleaner to use your own prefix. However the possibility of overwriting a private method is pretty small. Furthermore, apple has a lot of private methods without a prefix. – IluTov Dec 23 '12 at 10:44
  • @NSAddict sounds reasonable :) – Daij-Djan Dec 23 '12 at 10:58
  • @CodaFi you didnt get my point – Daij-Djan Dec 23 '12 at 10:58
2

One fairly solid solution to this is to use a static c method to do your initialization. All valid c is valid objective-c after-all.

So for example, CustomSuperclass.h:

#import <Foundation/Foundation.h>
@interface CustomSuperclass : NSObject
@property (nonatomic) NSUInteger integer;
@end

CustomSuperclass.m:

#import "CustomSuperclass.h"
@implementation CustomSuperclass
@synthesize integer = _integer;
static void initializeInstance(CustomSuperclass *self) {
    self.integer = 9;
}
-(id)init{
    if ((self = [super init])){
        initializeInstance(self);
    }
    return self;
}
@end

CustomSubclass.h:

#import "CustomSuperclass.h"
@interface CustomSubclass : CustomSuperclass
@end

CustomSubclass.m:

#import "CustomSubclass.h"
@implementation CustomSubclass
// Obviously we would normally use (CustomSubclass *self), but we're proving a point with identical method signatures.
void initializeInstance(CustomSuperclass *self) {
    self.integer = 10;
}
-(id)init{
    if ((self = [super init])){
        // We won't call this classes `initializeInstance()` function.
        // We want to see if this one overrides it's superclasses version.
    }
    return self;
}
@end

Now if you run code like this:

CustomSuperclass *instanceOfSuperclass = [[CustomSuperclass alloc] init];
NSLog(@"superclass integer:%i", instanceOfSuperclass.integer);

CustomSubclass *instanceOfSubclass = [[CustomSubclass alloc] init];
NSLog(@"subclass integer:%i", instanceOfSubclass.integer);

You will see:

superclass integer:9
subclass integer:9

If the subclass's version of initializeInstance() was the one that was used by the subclass (when calling [super init]) then the subclasses integer would be 10. This proves the initializeInstance() method of the superclass was not overridden (at least within the scope of the superclass) by the subclass's version.

Edit in response to comment:

I posted this answer with its solution, not because I think it is the best way to solve the problem, but because it meets the specification of the question. The question is essentially, "How do I force a dynamic language to act in a static way so that I don't have to follow normal naming/initialization convention?"

The best way of handling the situation is for your class to have a designated initializer that each of the initializers call. Like:

/* Designated initializer */
-(id)initWithFile:(NSString *)file andURL:(NSURL *)url{
    if ((self = [super init])){
        _url = url;
        _file = file;
        // Initialization of new instance code here.
    }
    return self;
}
-(id)initWithFile:(NSString *)file{
    return [self initWithFile:file andURL:nil];
}
-(id)initWithURL:(NSURL *)url{
    return [self initWithFile:nil andURL:url];
}

Designated initializers work for almost all cases. They're a bit cumbersome for UIView subclasses, since an IB UIView will have initWithCoder: called where a code instantiated UIView will/should have initWithFrame: called. This is a situation in which a common method might be written to finish up after the calls to [super initWith...] to reduce code duplication.

One could run into the problem you are having with overriding the common initialization method's name. This is where for most people a little security through obscurity works, for example:

-(void)initialize_CustomUIView{
    // Common init tasks
}
-(id)initWithFrame:(CGRect)frame{
    if ((self = [super initWithFrame:frame])){
        [self initialize_CustomUIView];
    }
    return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
    if ((self = [super initWithCoder:aDecoder])){
        [self initialize_CustomUIView];
    }
    return self;
}
-(id)init{
    if ((self = [super init])){
        [self initialize_CustomUIView];
    }
    return self;
}

But again, you asked specifically how to disable yourself from overriding the common initialization method, thus the static void initializeInstance() solution.

NJones
  • 27,139
  • 8
  • 70
  • 88
  • Yeah... I was thinking that the C keyword "static" would be useful for this, as it is used to hide visibility outside the current file scope. I'm not sure this is the best answer, but it does address the issue of "how do I prevent this from happening?" So thanks! – lar3ry Dec 22 '12 at 21:48
  • I would agree that a static method very-well might not be the best solution. I edited my answer with details. – NJones Dec 22 '12 at 23:34
1

there is no private or final keyword for methods in objC

just dont include them in the header...
maybe name them somehow __XY or so...


btw: in java private is not against overriding, thats just another 'sideeffect'. The keyword final is against overriding

Daij-Djan
  • 49,552
  • 17
  • 113
  • 135
  • I know it's a side-effect, but it's also effective in preventing subclasses from changing behavior. I also know I can use "ClassName_initialize" as a standard for method names or some such thing. Also, "final" in Java means prevent overriding. I just want a subclass that happens to implement a method of the same name to not accidentally clobber something in the base class. – lar3ry Dec 22 '12 at 18:28
  • ... eh yeah .. anyway there is nothing comparable – Daij-Djan Dec 22 '12 at 19:08
0

Look at this page It is about best objective c code practices including private methods:)

http://ironwolf.dangerousgames.com/blog/archives/913

Roma
  • 1,107
  • 9
  • 19
0

This has to be in the .m file:

@interface MyClass()

- (void) privateMethod1;

@end

@implementation MyClass

@end
Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
0

Methods defined in the @implementation or declared in a nameless category are not visible, but they can be executed. However, the real problem is that you want a initialize method per instance, but:

  • You want this method to be private but let users of this class to know that it exists, and they should override it. How? future you won't like this.
  • You want to override this method and call it from another overriden method. This results in double execution of the child method.

The convention you are trying to create doesn't make sense. You should give each initialize method a different name and call each from init.

Jano
  • 62,815
  • 21
  • 164
  • 192