> 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

Reply via email to