mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
164 lines
6.0 KiB
ReStructuredText
164 lines
6.0 KiB
ReStructuredText
:orphan:
|
|
|
|
=================================================
|
|
Integrating Swift Constructors with Objective-C
|
|
=================================================
|
|
|
|
.. warning:: This proposal was rejected, though it helped in the design of the
|
|
final Swift 1 initialization model.
|
|
|
|
Objective-C's "designated initializers pattern seems at first to
|
|
create a great deal of complication. However, designated initializers
|
|
are simply the only sound response to Objective-C's initialization rules,
|
|
which are the root cause of the complication.
|
|
|
|
This proposal suggests an approach to initialization that avoids the
|
|
problems inherent in Objective-C while still *allowing* Objective-C
|
|
programmers to pursue the designated initializer pattern on subclasses
|
|
of Swift classes.
|
|
|
|
The Root of the Problem
|
|
=======================
|
|
|
|
The root problem with Objective-C's initialization rules is that the
|
|
``init`` methods of a superclass automatically become public members
|
|
of its subclasses. This leads to a soundness problem:
|
|
|
|
.. parsed-literal::
|
|
|
|
@interface SuperClass
|
|
- initSuperClass
|
|
@end
|
|
|
|
@interface Subclass : Superclass
|
|
- (void)subclassMethod
|
|
@end
|
|
|
|
@implementation Subclass : Superclass
|
|
char\* **name**\ ; // never initialized
|
|
|
|
- (void)print { printf(\ **name**\ ); } // oops
|
|
@end
|
|
|
|
mySubclassInstance = [[Subclass alloc] initSuperClass]
|
|
|
|
Because there is no way to hide a superclass' ``init`` method from
|
|
clients, ensuring that subclass instances are properly initialized
|
|
requires overriding *every* superclass initializer in *every*
|
|
subclass:
|
|
|
|
.. parsed-literal::
|
|
|
|
@implementation Subclass : Superclass
|
|
char\* name;
|
|
- initSuperClass {
|
|
[super initSuperClass]; // Don't forget the superclass
|
|
**name = "Tino";**
|
|
}
|
|
- (void)print { printf(name); } // OK
|
|
@end
|
|
|
|
Following this rule is obviously tedious and error-prone for users.
|
|
Initialization is crucial to correctness, because it is where
|
|
invariants are established. It therefore should be no more complex
|
|
than everything else to reason about.
|
|
|
|
Also, it means adding an ``init`` method in a base class can be
|
|
API-breaking.
|
|
|
|
Furthermore, as John McCall pointed out recently, it forces
|
|
inappropriate interfaces on subclasses. For example, every subclass
|
|
of ``NSObject`` has a parameter-less ``init`` function, whether or not
|
|
there's an appropriate way to construct instances of that subclass
|
|
without parameters. As a result, class designers may be forced to
|
|
expose weaker invariants than the ones they could otherwise establish.
|
|
|
|
Exceptions to the Rule
|
|
======================
|
|
|
|
I exaggerated a little in the previous section: because overriding
|
|
*every* superclass initializer in *every* subclass is so tedious, the
|
|
Objective-C community has identified some situations where you don't
|
|
really need to override every ``init`` method:
|
|
|
|
1. When you know the default zero-initialization of a class' instance
|
|
variables is good enough, you don't need to override any ``init``
|
|
methods from your superclass.
|
|
|
|
2. If a given superclass' ``init`` method always calls another
|
|
``init`` method, you don't need to override the first ``init``
|
|
method because your instance variables will be initialized by your
|
|
override of the second ``init`` method. In this case, the first
|
|
(outer) ``init`` method is called a **secondary initializer**. Any
|
|
``init`` method that's not secondary is called a **designated
|
|
initializer**.
|
|
|
|
How to Think About This
|
|
=======================
|
|
|
|
At this point I'll make a few assertions that I hope will be
|
|
self-evident, given the foregoing context:
|
|
|
|
1. If the programmer follows all the rules correctly, one initializer
|
|
is as good as another: every ``init`` method, whether designated or
|
|
secondary, fully initializes all the instance variables. This is
|
|
true for all clients of the class, including subclassers.
|
|
|
|
2. Distinguishing designated from secondary initializers does nothing
|
|
to provide soundness. It's *merely* a technique for limiting the
|
|
tedious ``init`` method overrides required of users.
|
|
|
|
3. Swift users would not be well-served by a construction model that
|
|
exposes superclass ``init`` methods to clients of subclasses by
|
|
default.
|
|
|
|
Proposal
|
|
========
|
|
|
|
I suggest we define Swift initialization to be as simple and
|
|
easily-understood as possible, and avoid "interesting" interactions
|
|
with the more complicated Objective-C initialization process. If we
|
|
do this, we can treat Objective-C base classes as "sealed and safe"
|
|
for the purpose of initialization, and help programmers reason
|
|
effectively about initialization and their class invariants.
|
|
|
|
Here are the proposed rules:
|
|
|
|
* ``init`` methods of base classes defined in Objective-C are not, by
|
|
default, part of the public interface of a subclass defined in
|
|
Swift.
|
|
|
|
* ``init`` methods of base classes defined in Swift are not, by
|
|
default, part of the public interface of a subclass defined in
|
|
Objective-C.
|
|
|
|
* ``self.init(...)`` calls in Swift never dispatch virtually. We have a
|
|
safe model for "virtual initialization:" ``init`` methods can call
|
|
overridable methods after all instance variables and superclasses
|
|
are initialized. Allowing *virtual* constructor delegation would
|
|
undermine that safety.
|
|
|
|
* As a convenience, when a subclass' instance variables all have
|
|
initializers, it should be possible to explicitly expose superclass
|
|
init methods in a Swift subclass without writing out complete
|
|
forwarding functions. For example::
|
|
|
|
@inherit init(x:y:z) // one possible syntax
|
|
|
|
.. Note:: Allowing ``@inherit init(*)`` is a terrible idea
|
|
|
|
It allows superclasses to break their subclasses by adding
|
|
``init`` methods.
|
|
|
|
|
|
Summary
|
|
=======
|
|
|
|
By eliminating by-default ``init``\ method inheritance and disabling
|
|
virtual dispatch in constructor delegation, we give class designers
|
|
full control over the state of their constructed instances. By
|
|
preserving virtual dispatch for non-``self``, non-``super`` calls to
|
|
``init`` methods, we allow Objective-C programmers to keep using the
|
|
patterns that depend on virtual dispatch, including designated
|
|
initializers and ``initWithCoder`` methods.
|