1

Ok, this might not be possible, but I've got a class (called CompositeView) that's a subclasses UIView. It uses some core graphics work to produce a custom background based on some options. Not a huge class, but bound to grow as my demands change/increase/whatever. The problem I'm having is I use this class a lot, in a lot of different places. But in a few of the places I need it to be a subclass of UIScrollView instead of a UIView. Interestingly enough, I can simply change the superclass and it all works perfectly fine. But not only do I not want all my other views to be a UIScrollView, it also interferes with the operation of some of them. So I need a class that's sometimes a subclass of UIScrollView and sometimes a subclass of UIView.

For now, I've literally copied all of the interface/implementation of the CompositeView, changed the class name to CompositeScrollView, and changed it's inheritance to UIScrollView. It works fine, but now I've got two sets of code that do exactly the same thing, just inherited from different parent classes. This makes keeping them both up to date a pain.

Is there a better way to do this?

Adrian
  • 5,603
  • 8
  • 53
  • 85
Aaron Hayman
  • 8,492
  • 2
  • 36
  • 63
  • 1
    why do you need it to be a subclass of UIScrollView or of UIView (when UISCrollView is already a subclass of UIView)? More detail about what you are trying to accomplish is needed. – Joshua Smith Feb 24 '12 at 21:27

4 Answers4

4

Single inheritance languages force you to use delegation. You'd factor out the added functionality into a separate class that you instantiate for your derived classes and then write forwarding shims from the derived class to the instances. It's painful.

Objective C has protocols which would describe the added functions (any shims that are not overrides) and then the compiler would error-out if you didn't write the shim ... which you still have to do manually.

Objective C also has categories that allow you to extend existing classes but these can't be shared (you have to extend each class individually) so it doesn't really help.

smparkes
  • 13,807
  • 4
  • 36
  • 61
  • Yeah, actually that's probably the best idea so far. The composite view actually uses core graphics to create a UIImage it adds to a shared cache (shared by all views with the same options). All the view does is draw the image in drawInRect. I could probably offload a lot of that. I would make object creation a little more complicated but might be worth it. – Aaron Hayman Feb 24 '12 at 22:42
  • A category on UIView could work, actually, but they do not allow properties, so unless your added functionality adds no new state (just behavior) you cannot use them. +1! – Dan Rosenstark Feb 25 '12 at 04:34
  • Yeah, I didn't presume he wanted to add to all view and so was thinking of the use of category as a mixin to be applied to his classes derived from UIView and UIScrollView. But since categories themselves can't be applied to multiple classes, that went nowhere (even before looking at the adding ivar stuff). – smparkes Feb 25 '12 at 05:41
  • 1
    @smparkes A category wouldn't work in this instance per say, because yes, there's a lot of state that needs to be saved. I recognize that given the context of the question I asked, your response might not work if the bulk of the core graphics work was done in DrawRect. But in fact, the CG work is done in a UIGrapicsImageContext, which is easily exportable. So your solution does work in my case. In addition, forwarding methods is surprisingly easy. See my question: http://stackoverflow.com/questions/9415694/exposing-synthesizing-ivar-properties-in-objective-c/9419587#9419587 – Aaron Hayman Feb 25 '12 at 14:13
1

The best thing to do is impossible, of course: have a UIScrollView inherit from YOUR UIView subclass.

@smparkes' answer is good, but sometimes delegation does not do what you want, or it's too inconvenient. In this case, it's probably the latter.

Consider using the thing as a UIScrollView everywhere, but breaking the functionality that you don't need. UIScrollView instances act exactly like UIView instances -- well, they ARE UIView instances -- so you might just resolve this simple problem, "interferes with the operation of some of them" and go on your way. Shut off zoom, shut off scrolling, etc...

Unfortunately, this is the reality of single inheritance languages. Whatever you do, do not try to solve this with anything like changing the isa. Should you ever have any success, it will not be lasting. Objective-C is only slightly dynamic and does not allow for this kind of thing to be used seriously by regular programmers.

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
  • 1
    FWIW, I'd probably try disabling all the `UIScrollView` stuff first, too. That said, `UIScrollView`s are beasts and I might also consider it worth the extra shim code to avoid them except where I really needed them. – smparkes Feb 24 '12 at 22:17
  • I'd considered disabling UIScrollView, but the truth is, UIScrollView adds some weight I don't want. I originally wrote the composite view to speed up some animations on slower devices (which it did do). Using UIScrollView defeats that a little (not much, but enough). – Aaron Hayman Feb 24 '12 at 22:35
  • @AaronHayman empirically using UIScrollView defeats that? I agree it seems heavy, but is it? – Dan Rosenstark Feb 24 '12 at 22:53
  • @Yar A little, like I said, it's nothing major. But I can have as many as 200 views on the screen at once (iPad...maybe 50 on iPhone). It adds up. What makes it 'worse' is the user can place as many as they want. So it makes sense to keep it as light weight as possible. – Aaron Hayman Feb 24 '12 at 23:59
  • @AaronHayman I'm just saying try 1000 or 2000 if you haven't already and MAYBE you'll be surprised. Or probably not ;) – Dan Rosenstark Feb 25 '12 at 04:31
0

Ok, maybe this is totally crazy, but is ISA switching an option?

object->isa = [SomeClass class];

See: Objective-C: How to change the class of an object at runtime?

If you implemented a UIView-subclass that knew how to switch its ISA pointer to the UIScrollView-subclass, you would only have to deal with one class and could even decide dynamically which of the views you want at runtime.

Please note, that this is purely theoretical. I have never used ISA switching in live code and I personally don't think it makes for a good design :P

EDIT: But again, it isn't reducing any redundancies ... I've read a bit more into the topic and it really doesn't seem to be recommendable (memory structure of old object stays unchanged e.g.)

Community
  • 1
  • 1
Julian
  • 1,573
  • 2
  • 22
  • 36
-1

Yes, you may be interested in using Class-cluster. This can produce objects let's say MyCompositeClass which will produce either MyCompositeScrollClass objects or MyCompositeViewClass objects.

Apple uses class cluster a lot for instance in NSArray, when you use it, behind the scene your manipulating different objects. The difference is based on the size of the array, for instance for some small arrays NSArray will instanciate a class that is specialized in small data structure, etc...

This has the advantage of having nice performance and the complexity is totally hidded from the user by this concept of class cluster.

I invite you to read some documentation about that, it might be more understandable. https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/ClassCluster.html

Hope this was helpful :)

Ganzolo
  • 1,394
  • 12
  • 23
  • I don't know who puts -1... But if he or she can elaborate on that I'd be interested to know where was my mistake.. – Ganzolo Feb 25 '12 at 11:59