Why doesn't Objective-C support private methods?
Asked Answered
B

10

124

I've seen a number of strategies for declaring semi-private methods in Objective-C, but there does not seem to be a way to make a truly private method. I accept that. But, why is this so? Every explanation I've essentially says, "you can't do it, but here's a close approximation."

There are a number of keywords applied to ivars (members) that control their scope, e.g. @private, @public, @protected. Why can't this be done for methods as well? It seems like something the runtime should be able to support. Is there an underlying philosophy I'm missing? Is this deliberate?

Buoyancy answered 28/1, 2010 at 22:56 Comment(4)
Good question, btw.Trossachs
To your edits: yes, runtime enforcement would be very expensive for very little benefit. Compile-time enforcement would be a welcome addition to the language and is quite achievable. Yes, it could be circumvented at runtime, but that's fine. The programmer is not an enemy. The goal of scope is to help protect the programmer from error.Temporize
You can't "skip the runtime" — the runtime is what does message dispatch.Culch
"There would be no way for a private method to short-circuit this check" Did you mean "circumvent"? ;-)Semiskilled
T
103

The answer is... well... simple. Simplicity and consistency, in fact.

Objective-C is purely dynamic at the moment of method dispatch. In particular, every method dispatch goes through the exact same dynamic method resolution point as every other method dispatch. At runtime, every method implementation has the exact same exposure and all of the APIs provided by the Objective-C runtime that work with methods and selectors work equally the same across all methods.

As many have answered (both here and in other questions), compile-time private methods are supported; if a class doesn't declare a method in its publicly available interface, then that method might as well not exist as far as your code is concerned. In other words, you can achieve all of the various combinations of visibility desired at compilation time by organizing your project appropriately.

There is little benefit to duplicating the same functionality into the runtime. It would add a tremendous amount of complexity and overhead. And even with all of that complexity, it still wouldn't prevent all but the most casual developer from executing your supposedly "private" methods.

EDIT: One of the assumptions I've noticed is that private messages would have to go through the runtime resulting in a potentially large overhead. Is this absolutely true?

Yes, it is. There's no reason to suppose that the implementor of a class would not want to use all of the Objective-C feature set in the implementation, and that means that dynamic dispatch must happen. However, there is no particular reason why private methods couldn't be dispatched by a special variant of objc_msgSend(), since the compiler would know that they were private; i.e. this could be achieved by adding a private-only method table to the Class structure.

There would be no way for a private method to short-circuit this check or skip the runtime?

It couldn't skip the runtime, but the runtime wouldn't necessarily have to do any checking for private methods.

That said, there's no reason that a third-party couldn't deliberately call objc_msgSendPrivate() on an object, outside of the implementation of that object, and some things (KVO, for example) would have to do that. In effect, it would just be a convention and little better in practice than prefixing private methods’ selectors or not mentioning them in the interface header.

To do so, though, would undermine the pure dynamic nature of the language. No longer would every method dispatch go through an identical dispatch mechanism. Instead, you would be left in a situation where most methods behave one way and a small handful are just different.

This extends beyond the runtime as there are many mechanisms in Cocoa built on top of the consistent dynamism of Objective-C. For example, both Key Value Coding and Key Value Observation would either have to be very heavily modified to support private methods — most likely by creating an exploitable loophole — or private methods would be incompatible.

Trossachs answered 29/1, 2010 at 0:18 Comment(10)
+1 Objective-C is (in some ways) a "big-boy" language in which programmers are expected to exhibit a certain amount of discipline, rather than getting slapped on the hand by the compiler/runtime at every turn. I find it refreshing. For me, restricting method visibility during compilation/linking is sufficient and preferable.Poplin
That's exactly what I mean by Obj-C being "pythonic". Private methods are for when you don't trust your code being used properly by the client coder.Franzoni
More python being "obj-c-ic" :). Guido was quite proactive in maintaining Python on NeXT systems, including creating the 1st version of PyObjC. Thus, ObjC did influence python somewhat.Trossachs
+1 for the interesting historic tale of the benevolent dictator for life and his crossings with the mythical NeXT system.Franzoni
Thank you. Python, Java and Objective-C all have runtime object models that are pretty darned similar [to SmallTalk]. Java was quite directly derived from Objective-C. Python ran a parallel course more than one of inspiration, but Guido certainly studied NeXTSTEP closely.Trossachs
That's exactly why I love Objective-C! It's not messy. It's simple. I don't use scopes a lot, so this really makes the place beautiful. I even use Objective-C on Windows, even if that requires some steps and a library of my own (I love creating libraries!).Semiskilled
@randy: there should be more effort bringing Objective-C to Windows without cross-compiling. Any Cocoa-esque libraries would trump any UI library available on Windows.Grandfatherly
Except I don't trust its license and I would definitely prefer clang to gcc. But it's a good first step for sure.Grandfatherly
@bbum, i using one private property of UISplitviewcontroller like this [theSplitVC setValue:[NSNumber numberWithFloat:225.0] forKey:@"_masterColumnWidth"]; Now my question is apple will reject my app in code reviewing process because of i hack or change the behavior of standard control?Almondeyed
@iAmbitious Yes, that would be a violation of API usage.Trossachs
R
18

The runtime could support it but the cost would be enormous. Every selector that is sent would need to be checked for whether it is private or public for that class, or each class would need to manage two separate dispatch tables. This isn't the same for instance variables because this level of protection is done at compile time.

Also, the runtime would need to verify that the sender of a private message is of the same class as the receiver. You could also bypass private methods; if the class used instanceMethodForSelector:, it could give the returned IMP to any other class for them to invoke the private method directly.

Private methods could not bypass the message dispatch. Consider the following scenario:

  1. A class AllPublic has a public instance method doSomething

  2. Another class HasPrivate has a private instance method also called doSomething

  3. You create an array containing any number of instances of both AllPublic and HasPrivate

  4. You have the following loop:

    for (id anObject in myArray)
        [anObject doSomething];
    

    If you ran that loop from within AllPublic, the runtime would have to stop you sending doSomething on the HasPrivate instances, however this loop would be usable if it was inside the HasPrivate class.

Rutile answered 28/1, 2010 at 23:14 Comment(13)
I agree that there would be a cost involved. Perhaps it's not something you want on a handheld device. Can you support the enormous qualifier otherwise? Would the cost really matter on a desktop system?Buoyancy
Haven't though at that but you may be right. Objective-C has run-time message dispatch vs. the compile time method call of C++, so you would be talking about a compile time check vs. a run-time check.Windmill
It seems really strange that the main reason for not supporting private methods is due to performance. I think Apple simply didn't think private methods were very necessary, whether or not there was a performance hit. Otherwise they should have chosen a different language for their multi-billion dollar company.Franzoni
Adding private methods wouldn’t just complicate the implementation of the runtime, it would complicate the semantics of the language. Access control is a simple concept in static languages, but it’s a bad match for dynamic dispatch which would come with a lot of conceptual overhead and lead to many, many “why doesn’t this work?” questions.Pentheam
What would private methods buy you, really? Objective-C is, after all, a C based language. Thus, if someone has code running in your process, they can easily p0wnz your process regardless of how private or obfuscated any API might be.Trossachs
What Bill said. Obj-C has pointers. There's no way to keep your private methods walled off from other code in the same process.Inhibition
It wouldn't actually have to do the checks you suggest. Instead, the compiler could dispatch private method calls via objc_msgSendPrivate(), and the Class could grow another method table for that purpose. (Very late comment, I know, but I just happened across this question :-))Asbestosis
@alastair: It wouldn't work though. The compiler may not even know whether a given selector is public or private. If you have an object id someObject and you do [someObject performSelector:@selector(couldBePrivate)]; then the compiler could have absolutely no idea whether it is private or public. The check has to be done at runtime because that's when the dispatch is done.Rutile
@Rutile Actually you’d need a -performPrivateSelector: method (which would itself be private, or protected), so that actually isn’t as big a problem as you suggest. Similarly with invocations and the like; there would need to be a private variant of them, otherwise the ordinary version could trigger a private method, which would be wrong.Asbestosis
@alastair: That still requires at compile time that you know whether the selector is public or private. If I do [someObject someMethod] which table is used? The private table or the public table? Without statically determined context it's impossible to know, the decision therefore must be left to the runtime.Rutile
@Rutile Yes, it does require the compiler to work that out. But it already has to do that, actually, in order to e.g. get the correct return type. The only cases where guessing is necessary are those where the compiler doesn't have the object type (i.e. it just has id); and those, IMO, should always be regarded as attempts to invoke public methods.Asbestosis
@alastair: so what would stop me from just calling objc_msgSendPrivate() myself?Rutile
@Rutile Nothing. Indeed, that facility would have to be available so that KVO worked.Asbestosis
A
14

The answers posted thus far do a good job of answering the question from a philosophical perspective, so I'm going to posit a more pragmatic reason: what would be gained by changing the semantics of the language? It's simple enough to effectively "hide" private methods. By way of example, imagine you have a class declared in a header file, like so:

@interface MyObject : NSObject {}
- (void) doSomething;
@end

If you have a need for "private" methods, you can also put this in the implementation file:

@interface MyObject (Private)
- (void) doSomeHelperThing;
@end

@implementation MyObject

- (void) doSomething
{
    // Do some stuff
    [self doSomeHelperThing];
    // Do some other stuff;
}

- (void) doSomeHelperThing
{
    // Do some helper stuff
}

@end

Sure, it's not quite the same as C++/Java private methods, but it's effectively close enough, so why alter the semantics of the language, as well as the compiler, runtime, etc., to add a feature that's already emulated in an acceptable way? As noted in other answers, the message-passing semantics -- and their reliance on runtime reflection -- would make handling "private" messages non-trivial.

Amoakuh answered 28/1, 2010 at 23:50 Comment(10)
The problem with this approach is that someone can override private methods (even unknowingly) when they subclass your class. I suppose, essentially you have to choose names that are unlikely to be overridden.Rutile
Right dreamlax. This is my general complaint with Categories. They're very powerful, but they have a dark side too.Buoyancy
If you preface the methods with a _ followed by a 2+ capital letter prefix that you/your company uses, there will be very little chance for collision.Inchoative
Which feels like a hack on top of a hack to me.Buoyancy
@Mike: Apple have stated that method names with leading underscores are explicitly reserved for Apple only: developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…. The recommended alternative is to prefix with two capital letters and then an underscore.Rutile
Also, throw in your SSN after the method so other programmers know who wrote it. =D Namespaces, need them!!!Franzoni
If you are worried about the methods you define being overridden, you have not properly embraced the inherent levels of verbosity that Objective-C encourages...Fashoda
@Kendall Helmstetter Gelner "you have not properly embraced the inherent levels of verbosity" could you be more specific?Ivy
@Yar: Sure - the longer the method names you use, the less likely it is that someone else subclassing or creating a category will end up with the same method name and accidentally override your own method. I was half joking - but only half...Fashoda
@Kendall Helmstetter Gelner It's definitely true. I've found that for refactoring, having chosen unique (and perhaps bad/long) method names is a huge advantage in both Ruby and Objective-C. Personally I miss strong, static typing with good namespace support, but no one is asking me :)Ivy
D
8

The easiest solution is just to declare some static C functions in your Objective-C classes. These only have file scope as per the C rules for the static keyword and because of that they can only be used by methods in that class.

No fuss at all.

Distribute answered 4/2, 2010 at 11:15 Comment(2)
Do these have to be defined outside the @implementation section?Buoyancy
@Rob - no they can (and most likely should be) defined inside your classes implementation.Distribute
D
6

Yes, it can be done without affecting the runtime by utilizing a technique already employed by the compiler(s) for handling C++: name-mangling.

It hasn't been done because it hasn't been established that it would solve some considerable difficulty in the coding problem space that other techniques (e.g., prefixing or underscoring) are able to circumvent sufficiently. IOW, you need more pain to overcome ingrained habits.

You could contribute patches to clang or gcc that add private methods to the syntax and generated mangled names that it alone recognized during compilation (and promptly forgot). Then others in the Objective-C community would be able to determine whether it was actually worthwhile or not. It's likely to be faster that way than trying to convince the developers.

Delftware answered 29/1, 2010 at 0:58 Comment(5)
Compile time name mangling is much harder than one might assume as you have to mangle all method selectors, including those that may appear in XIB files and those that might be passed to things like NSSelectorFromString() as well as KeyValueCoding & friends...Trossachs
Yes, it would be more involved if you wanted to provide support for private methods across the toolchain. The compiler's symbol table would need to be stored, and accessible via some toolchain api. It wouldn't be the first time Apple had to incrementally roll out capabilities to developers that were sufficiently important as time and stability permitted. However, where private methods are concerned: it's questionable whether it's a sufficient win to undertake the effort. It's even more suspect that they should be accessible outside the class itself by these other mechanisms.Delftware
How would a compile-time-only enforcement + name-mangling stop someone walking a class's method list and finding it in there?Rutile
It wouldn't. My answer only addresses the question posed by the OP.Delftware
I have thought about having a go a trying to add private methods to Clang, one reason I thought private methods would be good is that they could be called directly like simple c functions and then you could have all the usual C function optimisations happening. I have never bothered because of time and you can already do this by just using c.Ayres
C
4

Essentially, it has to do with Objective-C's message-passing form of method calls. Any message can be sent to any object, and the object chooses how to respond to the message. Normally it will respond by executing the method named after the message, but it could respond in a number of other ways too. This doesn't make private methods completely impossible — Ruby does it with a similar message-passing system — but it does make them somewhat awkward.

Even Ruby's implementation of private methods is a bit confusing to people because of the strangeness (you can send the object any message you like, except for the ones on this list!). Essentially, Ruby makes it work by forbidding private methods to be called with an explicit receiver. In Objective-C it would require even more work since Objective-C doesn't have that option.

Culch answered 28/1, 2010 at 23:35 Comment(0)
F
1

It's an issue with the runtime environment of Objective-C. While C/C++ compiles down into unreadable machine code, Objective-C still maintains some human-readable attributes like method names as strings. This gives Objective-C the ability to perform reflective features.

EDIT: Being a reflective language without strict private methods makes Objective-C more "pythonic" in that you trust other people that use your code rather than restrict what methods they can call. Using naming conventions like double underscores is meant to hide your code from a casual client coder, but won't stop coders needing to do more serious work.

Franzoni answered 28/1, 2010 at 23:10 Comment(3)
And if consumers of your lib could reflect your private methods, couldn't they call them too?Fragonard
If they know where to look, isn't that how people have been using undocumented libraries on the iPhone? The issue with the iPhone is that you risk not having your app accepted for using any private API.Franzoni
If they access private methods through reflection, they get what they deserve. Any errors are not your fault.Hamstring
P
1

There are two answers depending on the interpretation of the question.

The first is by hiding the method implementation from the interface. This is used, typically with a category with no name (e.g. @interface Foo()). This permits the object to send those messages but not others - though one might still override accidentally (or otherwise).

The second answer, on the assumption that this is about performance and inlining, is made possible but as a local C function instead. If you wanted a ‘private foo(NSString *arg)‘ method, you would do void MyClass_foo(MyClass *self, NSString *arg) and call it as a C function like MyClass_foo(self,arg). The syntax is different, but it acts with the sane kind of performance characteristics of C++'s private methods.

Although this answers the question, I should point out that the no-name category is by far the more common Objective-C way of doing this.

Postcard answered 29/1, 2010 at 0:26 Comment(0)
H
0

Objective-C doesn't support private methods because it doesn't need them.

In C++, every method must be visible in the declaration of the class. You can't have methods that someone including the header file cannot see. So if you want methods that code outside your implementation shouldn't use, you have no choice, the compiler must give you some tool so you can tell it that the method must not be used, that is the "private" keyword.

In Objective-C, you can have methods that are not in the header file. So you achieve the same purpose very easily by not adding the method to the header file. There's no need for private methods. Objective-C also has the advantage that you don't need to recompile every user of a class because you changed private methods.

For instance variables, that you used to have to declare in the header file (not anymore), @private, @public and @protected are available.

Hamstring answered 12/4, 2014 at 8:4 Comment(0)
S
0

A missing answer here is: because private methods are a bad idea from an evolvability point of view. It might seem a good idea to make a method private when writing it, but it is a form of early binding. The context might change, and a later user might want to use a different implementation. A bit provocative: "Agile developers don't use private methods"

In a way, just like Smalltalk, Objective-C is for grown-up programmers. We value knowing what the original developer assumed the interface should be, and take the responsibility to deal with the consequences if we need to change implementation. So yes, it is philosophy, not implementation.

Shea answered 19/7, 2014 at 11:32 Comment(1)
I've up-voted this because it doesn't deserve the down-vote. There is, as Stephan says, an argument that "private" methods are not necessary, and it's similar to the arguments about dynamic vs static typing, in that, whatever your preferred conclusion, both sides have a point.Asbestosis

© 2022 - 2024 — McMap. All rights reserved.