0

I'm trying to create a static method on a generic class which takes in a closure as parameter and creates an instance of the class and passes it the closure. The catch is that I want to subclass this and ensure that the closure conforms to any subclass I use.

I know that you can use "Self" as a return type for a static method, but when I try to use it in the method header, I get the following error:

"'Self' is only available in a protocol or as the result of a method in a class"

I'd like to do something like this:

class GenericClass: NSObject {
    required override init() {
        super.init()
    }

    static func createAndExecuteAsync(block: (Self) -> Void) {
        DispatchQueue.global().async {
            let instance = self.init()
            block(instance)
        }
    }
}

class OtherClass: GenericClass {
    // ...
}

Somewhere else...

OtherClass.createAndExecuteAsync { (instance: OtherClass) in
    // Do stuff to instance
}

UPDATE:

Thanks to Hamish's solution in this post, I'm closer to a solution. I can use Self in the desired way if I first create a protocol for my generic class. However that forces me to make OtherClass final, which isn't desirable for my situation.

Without making OtherClass final, I get the following error:

Protocol 'GenericClass' requirement 'createAndExecuteAsync(block:)' cannot be satisfied by a non-final class ('OtherClass') because it uses 'Self' in a non-parameter, non-result type position.

Here's what it would look like:

protocol GenericClass {
    init()
    static func createAndExecuteAsync(block: @escaping (Self) -> Void)
}

extension GenericClass {
    static func createAndExecuteAsync(block: @escaping (Self) -> Void) {
        DispatchQueue.global().async {
            let instance = self.init()
            block(instance)
        }
    }
}

final class OtherClass : GenericClass {
    var myProperty = 1

    required init() { }
}

// Somewhere else...
OtherClass.createAndExecuteAsync { (instance) in
    instance.myProperty = 2
}
Community
  • 1
  • 1
Mel
  • 959
  • 5
  • 19
  • Strongly related (dupe?): [Self in init params](http://stackoverflow.com/q/40055862/2976878). As I show in [my answer](http://stackoverflow.com/a/42356615/2976878), you could use a protocol and put your `createAndExecuteAsync` method in an extension of that protocol. But ultimately, there's no real reason why this shouldn't be possible outside of a protocol extension. – Hamish Mar 08 '17 at 00:33
  • @Hamish Thank you for pointing me to your solution in that post. That forces me to use a final class, which is not ideal, but it got me much closer to a solution. – Mel Mar 08 '17 at 01:00
  • Ah yes, that's a rough edge that I mentioned in my answer – you'll have to remove `createAndExecuteAsync` from the protocol requirements, only have it in the extension. That should allow your code to compile with non-final classes :) – Hamish Mar 08 '17 at 09:14

1 Answers1

0

Perhaps you could use a global generic function with a slightly different usage syntax.

for example:

 func createAndExecuteAsync<T:GenericClass>(_ objectType:T.Type, _ block:@escaping (T) -> Void)
 {
     DispatchQueue.global().async 
     {
         let instance = T.init()
         block(instance)
     }
 }


 createAndExecuteAsync(OtherClass.self){ $0.myProperty = 2 }

 // instead of OtherClass.createAndExecuteAsync{ $0.myProperty = 2  }
Alain T.
  • 40,517
  • 4
  • 31
  • 51