Will Swift ever support optional methods without @objc?

I think you are fixating on my talk about imp caching instead of my main
point about setting up the state of my delegator to avoid unneeded work
when a registered delegate hasn't implement a delegation point. It an
unrelated topic to what is being discussed.

-Shawn

···

On Tue, Nov 15, 2016 at 5:27 PM Karl <razielim@gmail.com> wrote:

On 15 Nov 2016, at 19:38, Shawn Erickson <shawnce@gmail.com> wrote:

Using sub-protocols may be sufficient and make sense... to be honest I
haven't had the time to fully explore this space and convert some things I
have done in objective-c to pure swift. I do wonder how often that those
sub-protocols would degenerate into having single methods.

In a nut shell it isn't atypical for a delegate to only care about
"injecting itself" (e.g. implementing a delegate function) for a subset of
the available delegation points leaving the others unimplemented. In the
objective-c case the delegator can evaluate what delegation points a
delegate implements at time of delegate registration (or more dynamically
... however I often did imp caching for performance reasons in some of my
designs). This probe on delegate registration may make sense for the
delegator if additional bookkeeping, processing, state management, or
potentially whole code path/objects can be avoided if the delegate doesn't
implement a delegation point(s). If the delegation points happened to be
implemented using a default nop implementation this type of optimization
may not be possible.

In a nutshell I see and have the need for the delegator to know if the
delegate has actually provided an implementation of their own or not so I
can potentially leverage optimizations internal to my delegator. As a
delegate is also nice to know clearly what I have to implement or not and
the optional protocol member concept is one way of doing that, it would be
nice to have something like that to help delegate implementors.

I suggest mentally evaluating the delegation points of URLSession with the
perspective of the delegator (URLSession) being able to optimize what it
does based what it delegate has provided and implementation for. For
example the new metrics delegation point like could optimize away book
keeping and related processing if the delegate isn't interested.
Additionally look at it from the point of view of a delegate implementor
noting the despite already having some number of sub-protocols you still
often only implement one or two delegate points. Alternate swifty
implementations likely exist that would be interesting to explore to help
inform what makes sense as a language addition and/or help folks used to
"traditional" delegation pattern under Objective-C follow more Swifty
patterns going forward.

-Shawn

On Tue, Nov 15, 2016 at 9:24 AM Karl <razielim@gmail.com> wrote:

On 15 Nov 2016, at 16:46, Shawn Erickson <shawnce@gmail.com> wrote:

This has been discussed somewhat heavily in the past and nothing yet has
really moved forward on it. I do think a good way of doing something like
this would be helpful. I have resulted to defining an interface with an
extension that provides empty defaults and for each function a match bool
var exists to imply if it exists or not. The code accepting a delegate can
probe these bool vars to configure itself to efficiently operate based on
knowledge about what the delegate expects (some missing from most proposed
solutions other then @objc optional).
On Tue, Nov 15, 2016 at 6:59 AM Karl via swift-evolution < > swift-evolution@swift.org> wrote:

On 15 Nov 2016, at 12:22, Haravikk via swift-evolution < > swift-evolution@swift.org> wrote:

On 15 Nov 2016, at 07:53, Rick Mann via swift-evolution < > swift-evolution@swift.org> wrote:

On Nov 14, 2016, at 22:51 , Charlie Monroe via swift-evolution < > swift-evolution@swift.org> wrote:

One major example is the NS/UITableViewDataSource or Delegate - there are
many many methods that you don't need to implement, hence are optional.

But I think that this was partially solved by default implementation of
protocol methods, which pretty much does what you want...

I just realized I only responded to someone else, and not the whole list.
It does, but it forces me to make the return value of the protocol method
optional, so that the default implementation can return nil.

In the end, I guess that's not so bad, since I'm not happy with the entire
approach, but it'll do for now.

What's different about having the method return nil vs being optional?
You're attempting to call it either way, and presumably need some means of
handling the return value, except in Swift it's all nice and explicit and
easy to put in a conditional like:

if let result = myObject.someOptionalMethod() { /* Do some stuff */ }
print(myObject.someOptionalStringMethod() ?? "")

And so-on. If you need a method to be both optional, and return a nilable
result then you can use a double optional like so:

if let result = myObject.someDoubleOptionalMethod() { // Method was
implemented
if let value = result { // Method returned a value
/* Do some stuff */
}
}

By defining the methods as returning an Optional and throwing in default
implementations you can specify fewer, bigger protocols and make clear what
the requirements really are, though personally given the choice I'd prefer
a dozen smaller protocols that are absolutely explicit in what they do.

But yeah, I think the tools you need are all there already; maybe there's
an argument to be made for allowing default return values on protocol
methods, to reduce the boiler-plate?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think there is a difference between:

- A method which returns an optional result, and
- An optional method which, if present, always returns a result

Perhaps not so much of a difference at the usage site (it’s just a
question of placing a ? for optional chaining), but semantically and when
conforming to the protocol, they mean different things.

- Karl

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

If you don’t mind me asking, what is your use-case?

Even though I think "optional methods" and “methods returning optionals”
are different things, I don’t really have any examples where optional
methods are better than sub-protocols.

e.g.

// Core callbacks
protocol MyDelegate { }

// Optional callbacks, added like a mixin
protocol MyDelegateWithExtras : MyDelegate { }

// Some more optional callbacks
protocol MySubDelegate : MyDelegate {}

class DelegateImpl : MySubDelegate, MyDelegateWithExtras {
  // Implement all core + optional callbacks
}

var d : MyDelegate = DelegateImpl()

if let extras = d as? MyDelegateWithExtras {
    // invoke optional functionality
}

I don’t know what the overhead of the as? call is, but it’s almost
certainly less than an Obj-C `respondsToSelector` call. Depending on
whether you need to swap the delegate for objects of different types, you
could also use generics to optimise the checks (and possibly more) away.

- Karl

You sometimes needed those kind of caching techniques in Objective-C,
because it’s such a dynamic language. The question of “does this object
respond to this selector” has many complex considerations. For one thing,
objects may add and remove methods (or have it done to them) at any time.
Objects can even synthesise implementations the first time they receive a
selector. It’s been optimised massively over the years, but it can still be
a pretty slow operation -- and since hardly anything actually makes use of
those features it’s common to ask once and cache the responses in a
bitmask. In Objective-C, asking whether or not an object conforms to a
protocol just cascades in to a bunch of calls to “respondsToSelector”, so
it’s also very painful.

The Swift runtime doesn’t have those dynamic features. If you added any
methods to a bridged Swift object via the Obj-C runtime, those methods
wouldn’t be callable from your Swift code anyway, so we never have to worry
about that. Protocols in Swift are not the loose contracts of Objective-C;
you can create empty “marker” protocols and different objects may or may
not conform to it, even though it has no functional requirements. That
means testing for conformance should be much faster, too (and covers all
requirements at once).

Protocol dispatch in Swift is fast, there is something called a “protocol
witness table” (vtable + pointer + lifetime func ptrs), which is what is
actually getting stored when you declare a variable with only a protocol
for a type. So there isn’t really a huge runtime cost from the dispatch
anymore either, and you don’t need to explicitly resolve and store the IMPs
yourself to get decent performance.

- Karl

Huh. I’ve been using Objective-C for maybe 15 years and I’ve always avoided “conformsToProtocol” because I didn’t think it gave a strong contract like that. You learn something new every day :)

- Karl

···

On 16 Nov 2016, at 03:42, Charles Srstka <cocoadev@charlessoft.com> wrote:

On Nov 15, 2016, at 7:27 PM, Karl via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

In Objective-C, asking whether or not an object conforms to a protocol just cascades in to a bunch of calls to “respondsToSelector”, so it’s also very painful.

This isn’t true; Objective-C stores a list of protocols that a class conforms to, so -respondsToSelector: does not need to be called when checking protocol conformance (also: simply adopting a protocol’s methods will not cause a class to conform to the protocol).

You can test this yourself:

import <Foundation/Foundation.h>

@protocol P
  
- (void)foo;
  
@end

@interface C: NSObject<P>

- (void)foo;

@end

@implementation C

- (void)foo {
  NSLog(@"foo");
}

- (BOOL)respondsToSelector:(SEL)selector {
  NSLog(@"respondsToSelector: called");
  
  return [super respondsToSelector:selector];
}

@end

int main(int argc, char *argv) {
  @autoreleasepool {
    C *c = [C new];
    
    NSLog(@"C is P: %@", [c conformsToProtocol:@protocol(P)] ? @"YES" : @"NO");
  }
}

The output is only:

C is P: YES

The log we put in -respondsToSelector: never gets printed.

Charles

It’s the same point. I’ll try to make it more obvious: put your optional stuff in a sub-protocol and check for conformance to the sub-protocol. It’ll be fast and you don’t need any of the caching you do with Objective-C.

(delegate as? SpecialSubDelegate)?.extraCallback()

Should be as fast as whatever you were doing in Objective-C before. If you have larger blocks of potentially-avoidable work to do, wrap the delegate cast in an “if let” and do it there.

For example:

protocol ScrollViewDelegate : class {}
protocol ScrollObserver : ScrollViewDelegate {
    func scrollViewDidScroll(_ scrollview: UIScrollView)
}
protocol ScrollToTopController : ScrollViewDelegate {
    func scrollViewShouldScrollToTop(_ scrollview: UIScrollView) -> Bool
    func scrollViewDidScrollToTop(_ scrollview: UIScrollView)
}

Then, in your scrollview code, you’d declare your delegate as:

weak delegate : ScrollViewDelegate

and when you want to use the optional methods, as described earlier:

(delegate as? ScrollObserver)?.scrollViewDidScroll(self)

or if you have more work to do:

if let controller = delegate as? ScrollToTopController,
       controller.scrollViewShouldScrollToTop(self) {

  // Perform the scroll
  controller.scrollViewDidScrollToTop(self)
}

- Karl

···

On 16 Nov 2016, at 04:37, Shawn Erickson <shawnce@gmail.com> wrote:

I think you are fixating on my talk about imp caching instead of my main point about setting up the state of my delegator to avoid unneeded work when a registered delegate hasn't implement a delegation point. It an unrelated topic to what is being discussed.

-Shawn

On Tue, Nov 15, 2016 at 5:27 PM Karl <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 15 Nov 2016, at 19:38, Shawn Erickson <shawnce@gmail.com <mailto:shawnce@gmail.com>> wrote:

Using sub-protocols may be sufficient and make sense... to be honest I haven't had the time to fully explore this space and convert some things I have done in objective-c to pure swift. I do wonder how often that those sub-protocols would degenerate into having single methods.

In a nut shell it isn't atypical for a delegate to only care about "injecting itself" (e.g. implementing a delegate function) for a subset of the available delegation points leaving the others unimplemented. In the objective-c case the delegator can evaluate what delegation points a delegate implements at time of delegate registration (or more dynamically ... however I often did imp caching for performance reasons in some of my designs). This probe on delegate registration may make sense for the delegator if additional bookkeeping, processing, state management, or potentially whole code path/objects can be avoided if the delegate doesn't implement a delegation point(s). If the delegation points happened to be implemented using a default nop implementation this type of optimization may not be possible.

In a nutshell I see and have the need for the delegator to know if the delegate has actually provided an implementation of their own or not so I can potentially leverage optimizations internal to my delegator. As a delegate is also nice to know clearly what I have to implement or not and the optional protocol member concept is one way of doing that, it would be nice to have something like that to help delegate implementors.

I suggest mentally evaluating the delegation points of URLSession with the perspective of the delegator (URLSession) being able to optimize what it does based what it delegate has provided and implementation for. For example the new metrics delegation point like could optimize away book keeping and related processing if the delegate isn't interested. Additionally look at it from the point of view of a delegate implementor noting the despite already having some number of sub-protocols you still often only implement one or two delegate points. Alternate swifty implementations likely exist that would be interesting to explore to help inform what makes sense as a language addition and/or help folks used to "traditional" delegation pattern under Objective-C follow more Swifty patterns going forward.

-Shawn

On Tue, Nov 15, 2016 at 9:24 AM Karl <razielim@gmail.com <mailto:razielim@gmail.com>> wrote:

On 15 Nov 2016, at 16:46, Shawn Erickson <shawnce@gmail.com <mailto:shawnce@gmail.com>> wrote:

This has been discussed somewhat heavily in the past and nothing yet has really moved forward on it. I do think a good way of doing something like this would be helpful. I have resulted to defining an interface with an extension that provides empty defaults and for each function a match bool var exists to imply if it exists or not. The code accepting a delegate can probe these bool vars to configure itself to efficiently operate based on knowledge about what the delegate expects (some missing from most proposed solutions other then @objc optional).
On Tue, Nov 15, 2016 at 6:59 AM Karl via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 15 Nov 2016, at 12:22, Haravikk via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On 15 Nov 2016, at 07:53, Rick Mann via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 14, 2016, at 22:51 , Charlie Monroe via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

One major example is the NS/UITableViewDataSource or Delegate - there are many many methods that you don't need to implement, hence are optional.

But I think that this was partially solved by default implementation of protocol methods, which pretty much does what you want...

I just realized I only responded to someone else, and not the whole list. It does, but it forces me to make the return value of the protocol method optional, so that the default implementation can return nil.

In the end, I guess that's not so bad, since I'm not happy with the entire approach, but it'll do for now.

What's different about having the method return nil vs being optional? You're attempting to call it either way, and presumably need some means of handling the return value, except in Swift it's all nice and explicit and easy to put in a conditional like:

  if let result = myObject.someOptionalMethod() { /* Do some stuff */ }
  print(myObject.someOptionalStringMethod() ?? "")

And so-on. If you need a method to be both optional, and return a nilable result then you can use a double optional like so:

  if let result = myObject.someDoubleOptionalMethod() { // Method was implemented
    if let value = result { // Method returned a value
      /* Do some stuff */
    }
  }

By defining the methods as returning an Optional and throwing in default implementations you can specify fewer, bigger protocols and make clear what the requirements really are, though personally given the choice I'd prefer a dozen smaller protocols that are absolutely explicit in what they do.

But yeah, I think the tools you need are all there already; maybe there's an argument to be made for allowing default return values on protocol methods, to reduce the boiler-plate?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

I think there is a difference between:

- A method which returns an optional result, and
- An optional method which, if present, always returns a result

Perhaps not so much of a difference at the usage site (it’s just a question of placing a ? for optional chaining), but semantically and when conforming to the protocol, they mean different things.

- Karl

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

If you don’t mind me asking, what is your use-case?

Even though I think "optional methods" and “methods returning optionals” are different things, I don’t really have any examples where optional methods are better than sub-protocols.

e.g.

// Core callbacks
protocol MyDelegate { }

// Optional callbacks, added like a mixin
protocol MyDelegateWithExtras : MyDelegate { }

// Some more optional callbacks
protocol MySubDelegate : MyDelegate {}

class DelegateImpl : MySubDelegate, MyDelegateWithExtras {
  // Implement all core + optional callbacks
}

var d : MyDelegate = DelegateImpl()

if let extras = d as? MyDelegateWithExtras {
    // invoke optional functionality
}

I don’t know what the overhead of the as? call is, but it’s almost certainly less than an Obj-C `respondsToSelector` call. Depending on whether you need to swap the delegate for objects of different types, you could also use generics to optimise the checks (and possibly more) away.

- Karl

You sometimes needed those kind of caching techniques in Objective-C, because it’s such a dynamic language. The question of “does this object respond to this selector” has many complex considerations. For one thing, objects may add and remove methods (or have it done to them) at any time. Objects can even synthesise implementations the first time they receive a selector. It’s been optimised massively over the years, but it can still be a pretty slow operation -- and since hardly anything actually makes use of those features it’s common to ask once and cache the responses in a bitmask. In Objective-C, asking whether or not an object conforms to a protocol just cascades in to a bunch of calls to “respondsToSelector”, so it’s also very painful.

The Swift runtime doesn’t have those dynamic features. If you added any methods to a bridged Swift object via the Obj-C runtime, those methods wouldn’t be callable from your Swift code anyway, so we never have to worry about that. Protocols in Swift are not the loose contracts of Objective-C; you can create empty “marker” protocols and different objects may or may not conform to it, even though it has no functional requirements. That means testing for conformance should be much faster, too (and covers all requirements at once).

Protocol dispatch in Swift is fast, there is something called a “protocol witness table” (vtable + pointer + lifetime func ptrs), which is what is actually getting stored when you declare a variable with only a protocol for a type. So there isn’t really a huge runtime cost from the dispatch anymore either, and you don’t need to explicitly resolve and store the IMPs yourself to get decent performance.

- Karl

Again my point isn't worrying about point of calling out to the delegate
but configuring my delegator to avoid a body of work or state management
that is unneeded if the delegate doesn't care about some mix of potential
delegation points. I was trying to point out things to consider for those
stating "why not just provide a default implementation that is a nop, etc."
when in fact knowing if the delegate decided to not implement something can
be helpful for some delegators.

Anyway as stated earlier sub protocols likely are good enough however I am
concerned about it degenerating into a single func per sub protocol in not
atypical situations.

-Shawn

···

On Tue, Nov 15, 2016 at 8:10 PM Karl <razielim@gmail.com> wrote:

On 16 Nov 2016, at 04:37, Shawn Erickson <shawnce@gmail.com> wrote:

I think you are fixating on my talk about imp caching instead of my main
point about setting up the state of my delegator to avoid unneeded work
when a registered delegate hasn't implement a delegation point. It an
unrelated topic to what is being discussed.

-Shawn

On Tue, Nov 15, 2016 at 5:27 PM Karl <razielim@gmail.com> wrote:

On 15 Nov 2016, at 19:38, Shawn Erickson <shawnce@gmail.com> wrote:

Using sub-protocols may be sufficient and make sense... to be honest I
haven't had the time to fully explore this space and convert some things I
have done in objective-c to pure swift. I do wonder how often that those
sub-protocols would degenerate into having single methods.

In a nut shell it isn't atypical for a delegate to only care about
"injecting itself" (e.g. implementing a delegate function) for a subset of
the available delegation points leaving the others unimplemented. In the
objective-c case the delegator can evaluate what delegation points a
delegate implements at time of delegate registration (or more dynamically
... however I often did imp caching for performance reasons in some of my
designs). This probe on delegate registration may make sense for the
delegator if additional bookkeeping, processing, state management, or
potentially whole code path/objects can be avoided if the delegate doesn't
implement a delegation point(s). If the delegation points happened to be
implemented using a default nop implementation this type of optimization
may not be possible.

In a nutshell I see and have the need for the delegator to know if the
delegate has actually provided an implementation of their own or not so I
can potentially leverage optimizations internal to my delegator. As a
delegate is also nice to know clearly what I have to implement or not and
the optional protocol member concept is one way of doing that, it would be
nice to have something like that to help delegate implementors.

I suggest mentally evaluating the delegation points of URLSession with the
perspective of the delegator (URLSession) being able to optimize what it
does based what it delegate has provided and implementation for. For
example the new metrics delegation point like could optimize away book
keeping and related processing if the delegate isn't interested.
Additionally look at it from the point of view of a delegate implementor
noting the despite already having some number of sub-protocols you still
often only implement one or two delegate points. Alternate swifty
implementations likely exist that would be interesting to explore to help
inform what makes sense as a language addition and/or help folks used to
"traditional" delegation pattern under Objective-C follow more Swifty
patterns going forward.

-Shawn

On Tue, Nov 15, 2016 at 9:24 AM Karl <razielim@gmail.com> wrote:

On 15 Nov 2016, at 16:46, Shawn Erickson <shawnce@gmail.com> wrote:

This has been discussed somewhat heavily in the past and nothing yet has
really moved forward on it. I do think a good way of doing something like
this would be helpful. I have resulted to defining an interface with an
extension that provides empty defaults and for each function a match bool
var exists to imply if it exists or not. The code accepting a delegate can
probe these bool vars to configure itself to efficiently operate based on
knowledge about what the delegate expects (some missing from most proposed
solutions other then @objc optional).
On Tue, Nov 15, 2016 at 6:59 AM Karl via swift-evolution < > swift-evolution@swift.org> wrote:

On 15 Nov 2016, at 12:22, Haravikk via swift-evolution < > swift-evolution@swift.org> wrote:

On 15 Nov 2016, at 07:53, Rick Mann via swift-evolution < > swift-evolution@swift.org> wrote:

On Nov 14, 2016, at 22:51 , Charlie Monroe via swift-evolution < > swift-evolution@swift.org> wrote:

One major example is the NS/UITableViewDataSource or Delegate - there are
many many methods that you don't need to implement, hence are optional.

But I think that this was partially solved by default implementation of
protocol methods, which pretty much does what you want...

I just realized I only responded to someone else, and not the whole list.
It does, but it forces me to make the return value of the protocol method
optional, so that the default implementation can return nil.

In the end, I guess that's not so bad, since I'm not happy with the entire
approach, but it'll do for now.

What's different about having the method return nil vs being optional?
You're attempting to call it either way, and presumably need some means of
handling the return value, except in Swift it's all nice and explicit and
easy to put in a conditional like:

if let result = myObject.someOptionalMethod() { /* Do some stuff */ }
print(myObject.someOptionalStringMethod() ?? "")

And so-on. If you need a method to be both optional, and return a nilable
result then you can use a double optional like so:

if let result = myObject.someDoubleOptionalMethod() { // Method was
implemented
if let value = result { // Method returned a value
/* Do some stuff */
}
}

By defining the methods as returning an Optional and throwing in default
implementations you can specify fewer, bigger protocols and make clear what
the requirements really are, though personally given the choice I'd prefer
a dozen smaller protocols that are absolutely explicit in what they do.

But yeah, I think the tools you need are all there already; maybe there's
an argument to be made for allowing default return values on protocol
methods, to reduce the boiler-plate?
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think there is a difference between:

- A method which returns an optional result, and
- An optional method which, if present, always returns a result

Perhaps not so much of a difference at the usage site (it’s just a
question of placing a ? for optional chaining), but semantically and when
conforming to the protocol, they mean different things.

- Karl

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

If you don’t mind me asking, what is your use-case?

Even though I think "optional methods" and “methods returning optionals”
are different things, I don’t really have any examples where optional
methods are better than sub-protocols.

e.g.

// Core callbacks
protocol MyDelegate { }

// Optional callbacks, added like a mixin
protocol MyDelegateWithExtras : MyDelegate { }

// Some more optional callbacks
protocol MySubDelegate : MyDelegate {}

class DelegateImpl : MySubDelegate, MyDelegateWithExtras {
  // Implement all core + optional callbacks
}

var d : MyDelegate = DelegateImpl()

if let extras = d as? MyDelegateWithExtras {
    // invoke optional functionality
}

I don’t know what the overhead of the as? call is, but it’s almost
certainly less than an Obj-C `respondsToSelector` call. Depending on
whether you need to swap the delegate for objects of different types, you
could also use generics to optimise the checks (and possibly more) away.

- Karl

You sometimes needed those kind of caching techniques in Objective-C,
because it’s such a dynamic language. The question of “does this object
respond to this selector” has many complex considerations. For one thing,
objects may add and remove methods (or have it done to them) at any time.
Objects can even synthesise implementations the first time they receive a
selector. It’s been optimised massively over the years, but it can still be
a pretty slow operation -- and since hardly anything actually makes use of
those features it’s common to ask once and cache the responses in a
bitmask. In Objective-C, asking whether or not an object conforms to a
protocol just cascades in to a bunch of calls to “respondsToSelector”, so
it’s also very painful.

The Swift runtime doesn’t have those dynamic features. If you added any
methods to a bridged Swift object via the Obj-C runtime, those methods
wouldn’t be callable from your Swift code anyway, so we never have to worry
about that. Protocols in Swift are not the loose contracts of Objective-C;
you can create empty “marker” protocols and different objects may or may
not conform to it, even though it has no functional requirements. That
means testing for conformance should be much faster, too (and covers all
requirements at once).

Protocol dispatch in Swift is fast, there is something called a “protocol
witness table” (vtable + pointer + lifetime func ptrs), which is what is
actually getting stored when you declare a variable with only a protocol
for a type. So there isn’t really a huge runtime cost from the dispatch
anymore either, and you don’t need to explicitly resolve and store the IMPs
yourself to get decent performance.

- Karl

It’s the same point. I’ll try to make it more obvious: put your optional
stuff in a sub-protocol and check for conformance to the sub-protocol.
It’ll be fast and you don’t need any of the caching you do with Objective-C.

(delegate as? SpecialSubDelegate)?.extraCallback()

Should be as fast as whatever you were doing in Objective-C before. If you
have larger blocks of potentially-avoidable work to do, wrap the delegate
cast in an “if let” and do it there.

For example:

protocol ScrollViewDelegate : class {}
protocol ScrollObserver : ScrollViewDelegate {
    func scrollViewDidScroll(_ scrollview: UIScrollView)
}
protocol ScrollToTopController : ScrollViewDelegate {
    func scrollViewShouldScrollToTop(_ scrollview: UIScrollView) -> Bool
    func scrollViewDidScrollToTop(_ scrollview: UIScrollView)
}

Then, in your scrollview code, you’d declare your delegate as:

weak delegate : ScrollViewDelegate

and when you want to use the optional methods, as described earlier:

(delegate as? ScrollObserver)?.scrollViewDidScroll(self)

or if you have more work to do:

if let controller = delegate as? ScrollToTopController,
       controller.scrollViewShouldScrollToTop(self) {

  // Perform the scroll
  controller.scrollViewDidScrollToTop(self)
}

- Karl

Even if it does just become a single method, it’s not a cause for concern. In the example before, we gave some semantic meaning to those bundles of optional methods, so that’s a bit of a win. Also, what’s cool is that instead of the members being optional, the conformance to the sub-protocol is what is optional, and anything which declares conformance must implement all of the extra callbacks. The upshot of this is that the compiler is able to validate everything and produce an error if the extra callbacks change in the future.

For example:

class MyViewController : ScrollObserver {
    func scrollViewDidScroll(_ scrollview: UIScrollView) {
        // handle scroll
    }
}

Now we decide to change the function signature of this optional callback (for some reason. Let’s say we want to add a parameter…):

protocol ScrollObserver : ScrollViewDelegate {
    func scrollViewDidScroll(_ scrollview: UIScrollView, from lastLocation: CGPoint)
}

If the method was “optional”, objects would suddenly stop implementing this requirement and it would still be perfectly valid code. However, as a sub-protocol, we get proper validation and the compiler will produce an error until we update everything to handle the new signature.

I think this problem has actually come up with @obj-c protocols. Some signatures changed in Swift 3 due to API overlay improvements and some conformances silently failed to resolve, IIRC.

- Karl

···

On 16 Nov 2016, at 05:25, Shawn Erickson <shawnce@gmail.com> wrote:

Again my point isn't worrying about point of calling out to the delegate but configuring my delegator to avoid a body of work or state management that is unneeded if the delegate doesn't care about some mix of potential delegation points. I was trying to point out things to consider for those stating "why not just provide a default implementation that is a nop, etc." when in fact knowing if the delegate decided to not implement something can be helpful for some delegators.

Anyway as stated earlier sub protocols likely are good enough however I am concerned about it degenerating into a single func per sub protocol in not atypical situations.

-Shawn

Again my point isn't worrying about point of calling out to the delegate but configuring my delegator to avoid a body of work or state management that is unneeded if the delegate doesn't care about some mix of potential delegation points. I was trying to point out things to consider for those stating "why not just provide a default implementation that is a nop, etc." when in fact knowing if the delegate decided to not implement something can be helpful for some delegators.

Anyway as stated earlier sub protocols likely are good enough however I am concerned about it degenerating into a single func per sub protocol in not atypical situations.

-Shawn

Even if it does just become a single method, it’s not a cause for concern. In the example before, we gave some semantic meaning to those bundles of optional methods, so that’s a bit of a win. Also, what’s cool is that instead of the members being optional, the conformance to the sub-protocol is what is optional, and anything which declares conformance must implement all of the extra callbacks. The upshot of this is that the compiler is able to validate everything and produce an error if the extra callbacks change in the future.

For example:

class MyViewController : ScrollObserver {
    func scrollViewDidScroll(_ scrollview: UIScrollView) {
        // handle scroll
    }
}

Now we decide to change the function signature of this optional callback (for some reason. Let’s say we want to add a parameter…):

protocol ScrollObserver : ScrollViewDelegate {
    func scrollViewDidScroll(_ scrollview: UIScrollView, from lastLocation: CGPoint)
}

If the method was “optional”, objects would suddenly stop implementing this requirement and it would still be perfectly valid code. However, as a sub-protocol, we get proper validation and the compiler will produce an error until we update everything to handle the new signature.

We have the very same issue here with default implementations on protocols which is one of the reasons I've always supported the notion of somehow marking methods/vars that are supposed to implement a protocol, so that you get a warning/error when the protocol changes...

Example:

protocol ScrollObserver : ScrollViewDelegate {
    func scrollViewDidScroll(_ scrollview: UIScrollView)
}

extension protocol ScrollObserver {
    func scrollViewDidScroll(_ scrollview: UIScrollView) {
        /* no-op */
    }
}

class MyViewController : ScrollObserver {
    func scrollViewDidScroll(_ scrollview: UIScrollView) {
        // handle scroll
    }
}

Now the method declaration in the protocol changes to the suggested:

protocol ScrollObserver : ScrollViewDelegate {
    func scrollViewDidScroll(_ scrollview: UIScrollView, from lastLocation: CGPoint)
}

a) the declaration in the extension no longer provides the default implementation, yet the code compiles without a warning
b) when you fix the default implementation in the protocol extension, MyViewController still conforms to ScrollObserver, yet the default implementation is invoked instead.

···

On Nov 16, 2016, at 3:52 PM, Karl via swift-evolution <swift-evolution@swift.org> wrote:

On 16 Nov 2016, at 05:25, Shawn Erickson <shawnce@gmail.com <mailto:shawnce@gmail.com>> wrote:

I think this problem has actually come up with @obj-c protocols. Some signatures changed in Swift 3 due to API overlay improvements and some conformances silently failed to resolve, IIRC.

- Karl
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution