> On 16 Nov 2016, at 04:37, Shawn Erickson <[email protected]> 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 <[email protected]
> <mailto:[email protected]>> wrote:
>
>> On 15 Nov 2016, at 19:38, Shawn Erickson <[email protected]
>> <mailto:[email protected]>> 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 <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>>> On 15 Nov 2016, at 16:46, Shawn Erickson <[email protected]
>>> <mailto:[email protected]>> 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
>>> <[email protected] <mailto:[email protected]>> wrote:
>>>
>>>> On 15 Nov 2016, at 12:22, Haravikk via swift-evolution
>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>
>>>>
>>>>> On 15 Nov 2016, at 07:53, Rick Mann via swift-evolution
>>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>>
>>>>>
>>>>>> On Nov 14, 2016, at 22:51 , Charlie Monroe via swift-evolution
>>>>>> <[email protected] <mailto:[email protected]>> 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
>>>> [email protected] <mailto:[email protected]>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>> <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
>>> [email protected] <mailto:[email protected]>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>> <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
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution