17

Let's say a have a Superclass and an instance of this class superclassObject.
I create a derived ClassA.

How can I instantiate (initialize) an object classAObject of the derived class in a such way, that all the inherited fields are equal to ones of superclassObject?

Of course I can cycle through all the fields and manually copy the values like classAObject.property = [superclassObject.property copy]. But the problem with this approach is that I may not know (or have access to) all the ivars/properties of the superclass. Is there an easier (and more general) way?

It seems that I'm missing something really basic...

I'm trying to do this because I get an already initialized UIView (with frame, background color, autoresizing mask, etc.) and I want to replace it with my custom view with same parameters.

Update 1

I've found this question, and the answer there says that it

generally isn't supported in any OO language

however

In Objective-C it is possible in some cases

Ok, if it's not supported, what should I do? If it is supported, how can I achieve this?

Update 2

It seems I've found a solution to my particular case of this general problem, which I'll test and report that tomorrow.

However, this lead me to another idea: What if I use a NSCoder to encode the superclassObject (if it implements <NSCoding> of course), and then call [[ClassA alloc] initWithCoder:coder] with a coder that knows data from the encoded superclassObject? Disclaimer: Well, I'm not that familiar with coding concepts (or even not at all), so may be the last sentence is nonsense.

Community
  • 1
  • 1
adubr
  • 779
  • 7
  • 21
  • You initialize it has you would any normal object. The problems you are facing are probably with the superclass permissions and Superclass init function(constructor). – Radu May 17 '11 at 10:59
  • @Radu, you didn't understand my question. The ivars of the superclass instance are already set, but **not** to default values. I need to copy them. – adubr May 17 '11 at 11:08
  • Does your subclass add any instance variables of its own, or are the fields the same as the superclass? I.e., are you only creating new methods? – jscs May 17 '11 at 23:15
  • @Josh, yes, it does. If not, wouldn't it be enough just to cast the pointer like `classAObject = (ClassA *)superclassObject`? Anyway, do you mind explaining both cases or the difference between them? – adubr May 17 '11 at 23:29
  • No, the cast doesn't work. I just posted an answer based on the assumption that you weren't adding ivars; now irrelevant. I will update to explain; you can read the blog post I linked to in the meanwhile though, since I'm mostly referring to that. – jscs May 17 '11 at 23:46
  • I'll be quite interested to see what you've come up with! The `NSCoding` idea is inspired, but I haven't thought it through enough to figure out if it would work. Good luck! – jscs May 17 '11 at 23:53

3 Answers3

2

If I understand the question correctly, you have an established view which you want to change to a different class. Presumably for the reason of changing it's functionality.

Cloning class properties between classes and swapping instances is the only way to do this sort of thing in languages like Java.

But ... in Objective C we have Categories. If all you are trying to do is change behaviour then perhaps a solution might be to create a Category for UIView that performs the additional or overridden functionality you require.

Second thought is to look into why you are not creating the correct class in the first place and therefore avoid this whole problem.

drekka
  • 20,957
  • 14
  • 79
  • 135
  • +1 for "find a way around"; categories/class extensions aren't at all a good idea for overriding framework methods, though, only adding. Adding storage to a framework class, though legal, might not be good either. – jscs May 18 '11 at 00:01
  • @Josh, yep agree. My personal preference would be to look into why the wrong class was instantiated in the first place. better to catch the horse before it's bolted ;-) – drekka May 18 '11 at 01:16
0

It is definitely possible using ObjC runtime, but it will be a bit hairy... This is a link to Apple's docs: Objective-C Runtime and an example of its usage: Objective-C Runtime Programming.

You would iterate over all properties, check their attributes to find out those you can set (i.e. omitting readonly properties), get their getter method on the superclass, read value and set using setter method on the subclass.

In the specific case of UIView this may work - you will tweak until it works. Generally it may be difficult: what about ivars that are not exposed as properties? Do you want to copy them all? Then you would need to check properties defined in the class and all protocols the class implements. And I'm not sure if won't be necessary to iterate over all the superclasses of your superclass to get all defined properties and ivars.

But, still, it is possible.

UPDATE

If you would copy instance variables then probably you would be probably set without even touching properties - I assume that all state is saved in ivars eventually. So in this case also no need to touch protocol defined properties.

But the other solution that may work well enough is to just stick to the public interface (well actually part of it: just what appears to be data and not functionality): read data through properties of the superclass and set using properties of the derived class, again assuming that the classes will do the right thing with the data. In this case the protocol properties are equally important because the are also part of the public interface and their implementation can save state into instance variables.

Again, I wouldn't try this approach as a general solution to copy any class but in a case of duplicating the data of one specific class that might work well enough and you can easily and thorough test your case.

Tomasz Stanczak
  • 12,796
  • 1
  • 30
  • 32
  • In general case I suppose one would like to have all the data copied, including private ivars. But why should one care about properties from protocols? They're not backed with ivars, as far as I know, thus they have no data to copy. Or am I missing something? Well, anyway I agree that it's difficult. Though I've made one wild guess about this -- please check the updated question. – adubr May 17 '11 at 23:21
  • See update: protocol properties are also methods, you can implement them doing whatever you like including saving state to ivars. – Tomasz Stanczak May 18 '11 at 07:02
0

IFF your subclass adds only methods and not any storage (i.e., ivars) to the superclass, then you can use a semi-black-magic technique* called "isa-swizzling". Rob Napier mentions it, and links to a blog post that explains it all.

Each Objective-C instance has a chunk of storage in memory for its instance variables, but the implementation of the methods is all kept in another place, in the storage for the class object. The runtime uses an instance's isa pointer to get at the class object.

It is therefore possible, as demonstrated in the linked blog post, to transform an instance of one class into another provided they have exactly the same fields. As Rob warns, you cannot add ivars, only change the methods.

Casting (MyDerivedClass *)instanceOfSuperclass only has an effect at compile-time. All it does is make the compiler happy when it is comparing types: it doesn't affect the runtime lookup of methods, so your instanceOfSuperclass will still act like a Superclass.

EDIT: Just as a final thought, since we're already talking about dangerous techniques, perhaps you could create a helper class that would hold the ivars that you want to add to the subclass. Whew, now I've really gone mad!

I have never done this myself; I am only reporting a technique that may be useful to you. Please pay attention to all the warnings you see when this is discussed.


*Used for KVO, interestingly.

Community
  • 1
  • 1
jscs
  • 63,694
  • 13
  • 151
  • 195