-1

I'm creating multiple different UI subclasses which can be created using JSContext, and I'd like them all to inherit a certain set of methods.

The classes vary from NSButton to NSTextView. It would be nice to have a single subclass act as NSView parent class, but I can't figure out if this is possible — or how to begin searching.

For example:

@interface CustomButtonClass : NSButton

I'm looking for a way to make NSButton inherit from a custom NSView subclass.

Is there a way to go around this, or do I need to create multiple intermediate classes?

Tritonal
  • 607
  • 4
  • 16
  • 1
    Are you looking to effectively "inject" a class between `NSButton` and `NSView`, e.g. `CustomButtonClass : NSButton : CustomViewClass : NSView`? Class inheritance is a property of the class itself, and not of objects in that class — so doing something like this would affect _all_ `NSButton`s, `NSTextView`s, etc. in your process. Both extremely fragile, and risky. Can you be specific about what you're looking to have these types inherit? (There may be other ways to apply those implementations to your types.) – Itai Ferber Dec 07 '21 at 17:17
  • This is actually what I suspected. I'm looking to create a lot of convenience methods which can be exposed to `JSContext` using `JSExport`. As you described a very good case against my original goal, I guess the most sensible approach would be to create a `JSExport` protocol, which exposes much of the native core `NSView` stuff, and then just implement the rest by hand. – Tritonal Dec 07 '21 at 17:40
  • 1
    That sounds pretty reasonable. Depending on your specific needs, you may be able to automate at least part of this using the Obj-C runtime: if you create your own protocol which refines `JSExport`, you can iterate over all types conforming to that protocol and [`class_addMethod`](https://developer.apple.com/documentation/objectivec/1418901-class_addmethod)/[`method_setImplementation`](https://developer.apple.com/documentation/objectivec/1418707-method_setimplementation) a concrete implementation, mimicking what it would look like to inherit that implementation from another type. – Itai Ferber Dec 07 '21 at 18:30
  • I'm not sure how that might interplay with `JSExport`, but it might work. Alternatively, you could decorate the classes with macros to fill in consistent implementations. – Itai Ferber Dec 07 '21 at 18:32
  • It seems that by combining a universal, unifying protocol and some additional macros I'm able to do mostly what I was looking for. Thank you! – Tritonal Dec 07 '21 at 20:34
  • 2
    How about a category on `NSView`? – Willeke Dec 07 '21 at 21:13
  • Oh, of course. `NSView` category along with the protocol would be a much more reusable and maintainable solution than macros. It will also make it possible to do additional checks if the methods should be available or not, based on the view type. – Tritonal Dec 07 '21 at 21:18
  • I think you need composition instead of subclassing – Cy-4AH Dec 08 '21 at 10:07
  • 1
    just saying categories can inherit their own @protocols as well. – Ol Sen Dec 08 '21 at 13:45
  • Categories are the way to go here. Composition brings in more complexity and forwarding is unnecessary when trying to apply unifying methods to a bunch of semi-cross-compatible classes. I'll try to draft an answer here from my implementation. – Tritonal Dec 10 '21 at 19:10

1 Answers1

0

The answer was super obvious, but coming from different languages, I struggled to make the connection. Thanks to all the comments, it was super easy to figure out.

Rather than trying to insert an intermediate class somewhere in the inheritance tree, you can create a category for NSView. Categories can also include all the required protocols.

For example, in my case, I wanted to expose multiple NSView methods to JSContext, and create some of my own, which would work on any NSView-derived object.

@protocol JSInterfaceExports <JSExport>

// Anything you want to expose to JS
- (void)customMethod;

// You can also expose all the common NSView properties
// and methods to JS in this protocol
@property (nonatomic) NSRect frame;
- (void)removeFromSuperview;

@end

@interface NSView (JSInterface)

// Any custom methods and properties
- (void)customMethod;

@end

Include this file on any NSView subclass, make them comply to <JSInterfaceExports>, and you are set.

Tritonal
  • 607
  • 4
  • 16