Self in init parameters

I would like to use Self parameters in init like this:

class A { public init(finishBlock: ((_ operation: Self) -> Void)? = nil) {...} } 

I know that I could use "A" in this place, but I would like to achieve this if some class inherits from A, then the initializer will know the operation as a class type, and not just as A. So, for example if I wrote:

 class B: A { public init(finishBlock: ((_ operation: Self) -> Void)? = nil) {...} public func fooOnlyInB() {} } 

Then I could use:

 let b = B { (operation) in operation.fooOnlyInB() } 

How is this possible?

+5
source share
3 answers

Instead of using Self or A in each of the initializers, you can simply override each initializer of the subclass to use its own type as operation .

This works because A initialiser claims that operation must be a type that matches A , and when you override it, you have the right to use a subclass of A as operation . However, if you change operation to an unrelated type, such as String or Int , the compiler will not override the existing initializer.

First, define A using init :

 class A { init(finishBlock: ((_ operation: A) -> Void)?) {...} } 

Now, to create a subclass, you must override init using the subclass type as operation . In your call to super.init force upcast operation ( $0 ) into your subclass type and call finishBlock with this cast operation .

 class B: A { override init(finishBlock: ((_ operation: B) -> Void)?) { // Perform custom initialisation... super.init { finishBlock?($0 as! B) } } func fooOnlyInB() { print("foo") } } 

B initializer now passes B as operation , which means you no longer need to throw it! This is because you can override init with a more specific type, in this case B

 let b = B { operation in operation.fooOnlyInB() // prints "foo" } 
+2
source

No, I don’t think it’s possible, because you can only use Self in protocols, because it’s just a placeholder for the type that will conform to this protocol. This is not a real type of type A or B , so you cannot use it when defining classes, as if it were Any or AnyObject .

So, create a protocol that forces the corresponding types to implement the init(finishBlock:) initializer init(finishBlock:) :

 protocol BlockInitializable { init(finishBlock: ((_ operation: Self) -> Void)?) } class A: BlockInitializable { init() {} convenience required init(finishBlock: ((_ operation: A) -> Void)?) { self.init() finishBlock?(self) } } 

and you will get the following error:

Requirement for the protocol is "Blocked" init (finishBlock :) cannot be satisfied by a non-final class (A) , because it uses the "I" in the position of a non-parametric, non-result.

and the worse you lose the general type of Self operation with parameters.

To fix this, you must mark your class as final or use a struct, which is the final type. However, you will lose the ability to subclass these types. The reason why you should do this and why you cannot use Self in subclasses of types is explained here , so I recommend you take a look at it.

I will go over with the last option and use struct:

 protocol Initializable { init() } protocol BlockInitializable: Initializable { init(finishBlock: ((_ operation: Self) -> Void)?) } extension BlockInitializable { init(finishBlock: ((_ operation: Self) -> Void)?) { self.init() finishBlock?(self) } } 

then define A and B as struct:

 struct A: BlockInitializable { } struct B: BlockInitializable { func fooOnlyInB() { print("Only in B") } } 

and you can do the following:

 let blockA = A { operation in print("Only in A") } let blockB = B { operation in operation.fooOnlyInB() } 

You can download the playground from here .

+1
source

As others have said, you cannot do this directly, since Self is currently only available in protocols or as a return type of a method in a class.

However, you can get around this using the protocol extension to declare an initializer:

 protocol AProtocol : A {} // Pre Swift 5: // protocol AProtocol { // init() // } extension AProtocol { init(completion: ((Self) -> Void)? = nil) { self.init() completion?(self) } } class A : AProtocol { required init() {} } class B : A { func fooOnlyInB() { print("foo in B") } } let a = A(completion: { print($0) // A }) let b = B(completion: { $0.fooOnlyInB() // foo in B }) 

This, however, is not an ideal solution, since it makes it difficult to add your own stored properties for B , since they must be set to default values ​​to satisfy required init() .

But in fact, as far as I know, there is no real technical reason why you cannot use Self as a parameter to the argument of the closure method in the class.

The parameters of the function are contravariant, therefore (A) β†’ Void is a subtype of (B) β†’ Void . Therefore, ((A) β†’ Void) β†’ T can be redefined using ((B) β†’ Void) β†’ T (again applying contravariance), therefore, using Self as a function parameter is passed to the method should be completely legal.

It is simply that the language does not yet fully support ( SR-10121 ).

+1
source

Source: https://habr.com/ru/post/1258242/


All Articles