使用超类初始化对象实例
假设 a 有一个 Superclass
和该类的一个实例 superclassObject
。
我创建了一个派生的ClassA
。
如何以这样的方式实例化(初始化)派生类的对象 classAObject
,使得所有继承的字段都等于 superclassObject
的字段?
当然,我可以循环遍历所有字段并手动复制值,例如 classAObject.property = [superclassObject.property copy]。但这种方法的问题是我可能不知道(或无法访问)超类的所有 ivars/属性。 有没有更简单(也更通用)的方法?
看来我错过了一些非常基本的东西...
我试图这样做是因为我得到了一个已经初始化的 UIView (带有框架、背景颜色、自动调整大小蒙版等),并且我想用我的自定义视图替换它具有相同的参数。
更新1
我发现
通常任何 OO 语言都不支持
然而,
在 Objective-C 中,在某些情况下这是可能的
好吧,如果不支持,我该怎么办?如果受支持,我该如何实现这一目标?
更新 2
看来我已经找到了针对这个一般问题的特殊情况的解决方案,我将在明天进行测试并报告。
然而,这让我想到了另一个想法:如果我使用 NSCoder 来编码 superclassObject
(当然,如果它实现
),然后调用 [[ClassA alloc] initWithCoder:coder]
编码器知道来自编码的 superclassObject
数据? 免责声明:嗯,我不太熟悉编码概念(甚至根本不熟悉),所以最后一句话可能是废话。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果我正确理解了这个问题,那么你就有了一个既定的观点,你想改变它到另一个班级。大概是为了改变它的功能。
在类之间克隆类属性并交换实例是在 Java 等语言中执行此类操作的唯一方法。
但是……在 Objective C 中我们有类别。如果您想要做的只是更改行为,那么解决方案可能是为 UIView 创建一个类别,以执行您需要的附加或覆盖功能。
第二个想法是首先调查为什么你没有创建正确的类,从而避免整个问题。
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.
IFF 你的子类只添加方法,不向超类添加任何存储(即ivars),那么你可以使用一种半黑魔法技术*,称为“isa-swizzling” ”。 Rob Napier 提到了,并链接到一篇博客文章解释了这一切。
每个 Objective-C 实例在内存中都有一块存储空间来存放它的实例变量,但是方法的实现都保存在另一个地方,即类对象的存储空间中。运行时使用实例的 isa 指针来获取类对象。
因此,如链接的博客文章中所示,可以将一个类的实例转换为另一个类的实例只要它们具有完全相同的字段。正如 Rob 警告的那样,您不能添加 ivars,只能更改方法。
强制转换
(MyDerivedClass *)instanceOfSuperclass
仅在编译时有效。它所做的只是让编译器在比较类型时感到高兴:它不会影响方法的运行时查找,因此您的instanceOfSuperclass
仍然会像一样表现超类
。编辑:作为最后一个想法,由于我们已经在讨论危险技术,也许您可以创建一个辅助类来保存要添加到子类中的 ivars。唷,现在我真的已经疯了!我自己从来没有这样做过;我只是报告一种可能对你有用的技术。 请注意讨论此问题时看到的所有警告。
*用于KVO,有趣的是。
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 yourinstanceOfSuperclass
will still act like aSuperclass
.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.
使用 ObjC 运行时绝对是可能的,但它会有点麻烦......这是 Apple 文档的链接: Objective-C Runtime 及其用法示例:Objective-C 运行时编程。
您将迭代所有属性,检查它们的属性以找出可以设置的属性(即省略只读属性),在超类上获取它们的 getter 方法,读取值并使用子类上的 setter 方法进行设置。
在 UIView 的特定情况下,这可能会起作用 - 您将进行调整直到它起作用。一般来说,这可能很困难:那些没有作为属性公开的 ivars 又如何呢?您想全部复制吗?然后,您需要检查类中定义的属性以及该类实现的所有协议。我不确定是否不需要迭代超类的所有超类来获取所有定义的属性和 ivars。
但是,这仍然是可能的。
更新
如果您要复制实例变量,那么您可能会在不接触属性的情况下进行设置 - 我假设所有状态最终都保存在 ivars 中。因此在这种情况下也不需要触及协议定义的属性。
但是另一个可能工作得很好的解决方案是只坚持公共接口(实际上是它的一部分:只是看起来是数据而不是功能):通过超类的属性读取数据并使用派生类的属性进行设置,再次假设类将对数据执行正确的操作。在这种情况下,协议属性同样重要,因为它们也是公共接口的一部分,并且它们的实现可以将状态保存到实例变量中。
再说一遍,我不会尝试将此方法作为复制任何类的通用解决方案,但在复制一个特定类的数据的情况下,它可能工作得足够好,并且您可以轻松彻底地测试您的案例。
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.