2

Say you

protocol Able: class {
    var v:UIView? { get set }
    var x:CGFloat { get set }
}

then of course, when you use Able,

enter image description here

if you forget "v" or "x"...

it is an error. That's good.

So do this:

class ScreenThing: UIViewController, Able {
    @IBOutlet var v: UIView?
    var x: CGFloat = 0.0
}

All's well. That's great.

It is enforced that you specify "v" and "x" and indeed initialize them.

But. Try this...

var _H: UInt8 = 0

protocol Able: class {
}

extension Able where Self:UIViewController {

    var p:P {
    get {
        return objc_getAssociatedObject(self, &_H) as! P
        }
    set {
        objc_setAssociatedObject(self, &_H, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        __setter()
        }
    }
}

Able now has a property p.

You can use p perfectly either in functions in Able, or, in functions in ScreenThing. That's great.

However.....

When you do this.....

class ScreenThing: UIViewController, Able {
}

you do not get an error.

You can forget to initialize "p" (it will crash).

Indeed you don't have to specify "p" as a variable (as you must with "v" and "x").

Why is it so?

This seems like a huge problem.

Is there something different I have to do, to, make the compiler enforce "p", just as it of course normally enforces variables in protocols?


Another way to look at this question:

Given exactly my code above:

is there a way to enforce the compiler to need an initializer for "p" in the consumer class?

For example.

I tried this...

class ScreenThing: UIViewController, Able {
    var p:P
}

But that doesn't work.

(Strangely, that compiles - actually I don't know what the hell it's doing! It seems to be a "different" p from the p in the Extension. But in any event it doesn't enforce the need for an initializer.)

In short, again, is there something I could do or add, above, that would make the compiler enforce me initializing the pseudo-property-thing, just as of course it normally does when I put a property in the protocol such as "x" or "v".

?

  • maybe I have to add some ordinary property in the protocol (like "pp"), and somehow make p related to that in some way?? Just a thought.

(Footnote -- see this to understand the ": class" needed in the protocol above.)


Answering my own question:

My confusion above is that there is nothing to initialize. The var p:P in the extension (with the get and set code blocks) is simply two functions.

There's nothing to initialize.

So for example: in my extra question, I ask "how to force conforming classes initailize it on wake up?" That is meaningless. If anything, one could ask: "how to force conforming classes be sure to 'use those functions' on wake up?" - which has nothing to do with initialization.

Note too that my specific example code in the computed variable, happens to (unrelatedly) use a variable that doesn't get initialized - leading to confusion.

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • 2
    I'm not sure what you expect the compiler to do – `p` is not a protocol requirement (and even if it was, you've already given it a default implementation as a computed property), so what are you expecting the compiler to enforce you implement in `ScreenThing`? – Hamish Jan 22 '17 at 22:53
  • 1
    Swift is a relatively new language, with a compiler that is full of bugs in corner cases. I would file a bug report, and see what Apple has to say. – Sergey Kalinichenko Jan 22 '17 at 22:54
  • Hey @hamish - I'm not as expert as you man. But the simple fact is that - looking at the specific example - you don't have to initialize it at all. Note that the example given, it will crash when you run it. I've never seen an example of get-set idiom, in Swift, where you can "not initialize it" (!) So, I will consider carefully what you have said and study the matter man! – Fattie Jan 22 '17 at 23:18
  • 1
    @JoeBlow The "get-set idiom" you speak of is a [computed property](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID259) and is really nothing more than a pair of methods with some syntactic sugar applied to make them look like a property. There's no initialisation to be done, as there's no *storage* for the property `p` (again, just glorified methods). You have to initialise `v` and `x` because they're *stored* properties, therefore they have memory which Swift can enforce you initialise – Hamish Jan 22 '17 at 23:25
  • @Hamish - yeah, that's a great explanation; good one. ("they are just computed properties"). Dude, check the new part of the question I added..... – Fattie Jan 22 '17 at 23:26
  • 1
    @JoeBlow (cont.) Swift doesn't enforce you initialise the associated object because that's implemented in the Objective-C runtime – it's up to you to ensure that it has a value (or even better, you can add some logic to handle the case when `objc_getAssociatedObject` returns `nil`). – Hamish Jan 22 '17 at 23:27
  • 1
    Your update doesn't compile for me @JoeBlow – Swift complains that there are no initialisers (as it should). – Hamish Jan 22 '17 at 23:31
  • Right. I guess this is one thing that is confusing me @Hamish: say **in the protocol** you have a property `x {get set}`. Then in fact you **must** initialize that property in the consumer. However, if **in the extension** you have a property `x {get {...} set{...}}`, then, you *do not* have to initialize it in the consumer. That seems odd. Anyway - I guess that's how it is. – Fattie Jan 23 '17 at 00:28
  • 1
    @JoeBlow If you have `var p: P { get set }` in the protocol, the conforming type must satisfy this requirement (if it hasn't already been satisfied by the extension) with either a stored property *or* computed property. The former requires initialising, the latter doesn't. Try moving the `var p: P {get{...} set{...}}` from the protocol extension into `ScreenThing` – you still won't have to (and can't) initialise it. – Hamish Jan 23 '17 at 00:33
  • Yeah quite right @hamish. That's a great clarification. I'm sure these comments will be helpful to many. Hopefully you don't feel your time was wasted! – Fattie Jan 23 '17 at 00:39

1 Answers1

1

You don't have to implement p in the protocol's adopter, because the protocol extension has supplied an implementation. That is what a protocol extension is.

Simpler example:

protocol P {}
extension P {
    func greet() {print("hello")}
}
class C : P {}
C().greet()

Note that (1) that compiles even though C does not declare greet and (2) it runs even though C does not contain an implementation of greet. That's because that's the job of the protocol extension.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    Also `p` isn't even a protocol *requirement* in the first place ;) – Hamish Jan 22 '17 at 23:02
  • 1
    @Hamish I look at it differently. To me, protocol extension material is a requirement but it also _satisfies_ the requirement. It's a self-satisfying requirement! :) After all, exactly the same thing would happen if `greet` _were_ listed in the actual protocol. The first line of my code could be `protocol P {func greet()}` but that would change nothing; the extension supplies the implementation. – matt Jan 22 '17 at 23:04
  • 1
    True – it was really just the first line of your answer that prompted the comment (probably more of a nitpick), as it somewhat implied that if the protocol extension *hadn't* supplied an implementation, then you would have to implement `p` in the protocol adopter :) Although note that there are subtleties (which I'm sure you're probably aware of) around including requirements in the actual protocol declaration itself. By doing so, you're including it in a conforming type's protocol witness table, granting you dynamic dispatch when calling protocol requirements on a value typed as the protocol. – Hamish Jan 22 '17 at 23:11
  • @matt - It's an edge case, but I'd be wary about suggesting that people implement a default implementation in the protocol extension without explicitly declaring the method as part of the protocol. If you ever use this protocol as a type (which is a common pattern), it won't call honor overrides to this default implementation. See https://gist.github.com/robertmryan/671b7645d925d80b1eba41c6864f004e for an example of the problem. Note, this is only an issue if you may possibly override the default implementation, but I'd be careful about not declaring the requirements explicitly. – Rob Jan 23 '17 at 06:32
  • @Rob I didn't suggest anything of the sort. I just answered the question. I discussed an aspect of this behavior here: http://stackoverflow.com/a/31432610/341994 And see especially my book: http://www.apeth.com/swiftBook/ch04.html#_extending_protocols – matt Jan 23 '17 at 10:54