mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Diagnose a metatype reference that doesn't appear as part of a call or member reference, offering fixits to either default-construct the type or get at the metatype explicitly using '.self'. Also diagnose an attempt to refer to 'T.type' by fixiting it to '.self'. Swift SVN r14433
161 lines
5.9 KiB
ReStructuredText
161 lines
5.9 KiB
ReStructuredText
:orphan:
|
|
|
|
=================================================
|
|
Integrating Swift Constructors with Objective-C
|
|
=================================================
|
|
|
|
Objective-C's “designated inititalizers” pattern seems at first to
|
|
create a great deal of complication. However, designated initializers
|
|
are simply the only sane 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.
|