Dynamic method replacement


(Tino) #63

That seems to have a comparable result - but where and when would those properties be set, and how would they deal with self?
It would be necessary that the closures are static so that every instance will adopt the changed behavior, wouldn't it?

It's definitely a lot more work than the method outlined in the proposal, but unless someone wants to use dynamic on a large number of methods, I think this wouldn't be to bad (and you could use code generators for the boilerplate).


(Karl) #64

It's not a bug, it's a feature - per-instance swizzling :sweat_smile:

no but seriously, those are some fair points. I wonder if dynamic methods couldn't be exposed as mutable closures somehow using another spelling (e.g. MyType.DynamicMethods.doSomething = { ... }). That could allow you to ignore the self argument when calling as a method and allow re-swizzling at runtime.


(Tino) #65

Imho it's still an option... I don't know how big of a problem @objc really is in this context, and how many people would need to write stuff like

open class Swizzled {
	static public var xClosure: (Swizzled, Int) -> Int = {
		return $1
	}

	public func swizzledProtocolRequirementX(parameter: Int) -> Int {
		return Swizzled.xClosure(self, parameter)
	}
}

Another issue of this solution is that without +initialize, somebody has to actually do the "swizzling"... but I hope this can be solved in the future with some reflection & metaprogramming - maybe even before @objc is fully deprecated ;-)


(Arnold Schwaighofer) #66

I will updated the pitch to clarify this. Your example will be rejected because the generic signature does not match.

The @dynamicReplacement(for:) attributes indicates which dynamic declaration is replaced by the new declaration. This declaration must have the same type -- including generic signature and requirements -- and be on an extension of the owning type or at module scope for dynamic non-member declarations.

// Module A
extension Set where Element: SomeProtocol {
  dynamic func foo() { }
  dynamic func bar() { }
}

// Module B
extension Set {
  @ dynamicReplacement(for: "foo()")
  dynamic func myFoo() { }  // ERROR: signature of myFoo doesn't match signature of foo
}

extension Set where Element: SomeProtocol {
  @dynamicReplacement(for: "bar()")
  dynamic func myBar() { }   // okay: signatures match
}

// ModuleA
class MyThing<T> {
  static dynamic func doSomething() -> Int { return -1 }
}

// ModuleB
extension MyThing where T: MyProtocol {
  @dynamicReplacement(for: doSomething)
  func doSomethingVariant1() -> Int {  return 42 } / ERROR: signature of doSomethingVariant1 doesn't match signature of doSomething
}

(Arnold Schwaighofer) #67

I added the following section about the alternative solution to using dynamic.


(Suyash Srijan) #68

Seems like this has been merged into master a while ago, but since the attribute is marked UserInaccessible, I guess it'll be a while before its publicly available?


(Jeffrey Macko) #69

I now hope that this feature stay only available for Apple since there is no clear/real use case for it(outside of Apple), FWIW the consensus seem to be more against it than for it and for the record yes Iā€™m biased Iā€™m against it.

Merry Christmas to everyone and happy new year !


#70

+1 for me, this would be very useful.