来自NIB File的Subclassed UIView未键入子类 [英] Subclassed UIView from NIB File not typed to subclass
问题描述
我有一个NIB文件,其中一些类(SomeClassView:UIView)被设置为NIB顶级视图的自定义类。 SomeClass有IBOutlets,我用它来连接我在Interface Builder中列出的SomeClass的子视图。
I have a NIB file, with some class (SomeClassView : UIView) set as the custom class of the top level view of the NIB. SomeClass has IBOutlets that I use to hookup subviews of SomeClass that I lay out in Interface Builder.
我像这样实例化SomeClass:
I instantiate SomeClass like this:
- (id)initWithFrame:(CGRect)frame {
self = [[[[NSBundle mainBundle] loadNibNamed:@"SomeClassView" owner:nil options:nil] objectAtIndex:0] retain];
// "SomeClassView" is also the name of the nib
if (self != nil) {
self.frame = frame;
}
return self;
}
现在说我用SubClassView子类SomeClass。我向SubClassView添加了一个名为 - (void)foo的方法:
Now say I subclass SomeClass with SubClassView. I add a method to SubClassView called -(void)foo:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self != nil) {
[self foo];
}
return self;
}
此时我收到运行时错误: * 由于未捕获的异常'NSInvalidArgumentException'终止应用程序,原因:' - [SomeClassView foo:]:无法识别的选择器发送到实例0xAAAAAA'
At this point I get a runtime error: * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SomeClassView foo:]: unrecognized selector sent to instance 0xAAAAAA'
好像自我在SubClassView的initWithFrame中仍然设置为super,SomeClassView。解决这个问题的快速修复方法是更改SubClassView initWithFrame中的isa指针:
It seems as though "self" within initWithFrame of SubClassView is still set to the super, SomeClassView. A quick hack fix to work around this is to change the isa pointer within SubClassView initWithFrame:
- (id)initWithFrame:(CGRect)frame {
Class prevClass = [self class];
self = [super initWithFrame:frame];
if (self != nil) {
isa = prevClass;
[self foo]; // works now
}
}
这不是一个理想的解决方法,因为每次我子类化时都必须更新isa,或者甚至可能有不同的init方法,我也必须更新。
It's not an ideal workaround, since I have to update isa each time I subclass, or there could even be different init methods which I'll also have to update.
1)理想情况下,是否有一种简单的方法可以解决这个问题,纯粹是通过代码解决?也许我设置自己的方式=加载的笔尖?
1) Ideally, is there an easy way to fix this, purely by code? Maybe with the way I'm setting self = the loaded nib?
2)是否有替代架构能够更好地满足我的目标?一个示例是将所有者设置为self,但是您必须手动设置所有属性/出口映射。
2) Is there an alternative architecture that works better for what I'm trying to do? One example is to set the owner to self, but then you'd have to set all the property/outlet mappings manually.
推荐答案
如果您的子类具有除 SomeClassView <中声明的实例变量之外的实例变量,则交换isa指针是一个问题/ code>。请注意,您的nib文件有一个类型为
SomeClassView
的对象,这意味着,在加载nib文件时,nib加载器将分配该类型的对象并从中取消它nib文件。将isa指针更改为 [SubViewClass类]
暂时不会使它成为 SubViewClass
类型的对象本身nib loader分配的是一个 SomeClassView
对象。
Swapping isa pointers is a problem if your subclasses have instance variables other than those declared in SomeClassView
. Note that your nib file has an object of type SomeClassView
, which means that, upon loading the nib file, the nib loader will allocate an object of that type and unmarshall it from the nib file. Changing the isa pointer to [SubViewClass class]
temporarily won’t make it an object of type SubViewClass
per se since what the nib loader allocates is a SomeClassView
object.
这就是说,我认为没有一个可靠的和自动的使用nib文件的方法包含在nib加载时需要更改类型的对象。
That said, I don’t think there’s a reliable and automatic way to use nib files containing objects whose types need to be changed upon nib loading.
你可以做的是让你的 SomeClassView
对象声明符合某些协议的委托。该协议将定义可以扩展的 SomeClassView
中的行为方法。例如,
What you could do is to have your SomeClassView
objects declare a delegate conforming to some protocol. This protocol would define methods for behaviour in SomeClassView
that can potentially be extended. For instance,
@protocol SomeClassViewDelegate
@optional
- (void)someClassViewDidAwakeFromNib:(SomeClassView *)someClassView;
@end
而不是继承 SomeClassView
,你有任意对象执行你当前在 SubClassView
中的任何自定义行为。例如,
Instead of subclassing SomeClassView
, you’d have arbitrary objects performing whatever custom behaviour you currently have in SubClassView
. For instance,
@interface SubClassViewBehaviour : NSObject <SomeClassViewDelegate>
…
@end
@implementation SubClassViewBehaviour
- (void)someClassViewDidAwakeFromNib:(SomeClassView *)someClassView {
// whatever behaviour is currently in -[SubClassView foo]
}
@end
A SubClassViewBehaviour
对象将在代码中创建,并在加载nib文件或任何其他IB代理对象时设置为nib文件的所有者。 SomeClassView
会有一个委托出口连接到文件的所有者/代理对象,并且它会在适当的位置调用委托方法。例如,
A SubClassViewBehaviour
object would be created in code and set as the nib file’s owner upon loading the nib file, or any other IB proxy object for that matter. SomeClassView
would have a delegate outlet connected to file’s owner/proxy object, and it’d invoke the delegate methods in the appropriate places. For instance,
@implementation SomeClassView
- (void)awakeFromNib {
SEL didAwakeFromNib = @selector(someClassViewDidAwakeFromNib:);
if ([[self delegate] respondsToSelector:didAwakeFromNib]) {
[[self delegate] performSelector:didAwakeFromNib withObject:self];
}
}
@end
还有一句话:你的代码当前泄漏了一个视图对象,因为实例化了两个对象:一个通过 + alloc
代码,另一个通过nib加载。您将后者分配给 self
,因此通过 + alloc
创建的那个正在泄漏。此外,我相信您错过了第三个代码段中对 super
的调用。
One further remark: your code currently leaks a view object since two objects are being instantiated: one via +alloc
in your code and another one via nib loading. You’re assigning the latter to self
, hence the one created via +alloc
is leaking. Also, I believe you’ve missed a call to super
in your third code snippet.
这篇关于来自NIB File的Subclassed UIView未键入子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!