2011-04-05 41 views
4

我有一个NIB文件,用类(SomeClassView:UIView的)设置为自定义类的NIB的顶层视图。 SomeClass有IBOutlets,用于连接我在Interface Builder中布局的SomeClass的子视图。从NIB文件子类的UIView不是打字子类

我实例SomeClass的是这样的:

- (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; 
} 

现在说我继承SomeClass的与SubClassView。我添加到SubClassView调用的方法 - (空)富:

- (id)initWithFrame:(CGRect)frame { 
    self = [super initWithFrame:frame]; 
    if (self != nil) { 
     [self foo]; 
    } 
    return self; 
} 

在这一点上,我得到一个运行时错误:*终止应用程序由于未捕获的异常“NSInvalidArgumentException”,原因是:“ - [SomeClassView富: ]:无法识别的选择器发送到实例0xAAAAAA'

似乎在SubClassView的initWithFrame内的“自我”仍然设置为超级SomeClassView。快速破解补丁来解决,这是改变SubClassView initWithFrame内的isa指针:

- (id)initWithFrame:(CGRect)frame { 
    Class prevClass = [self class]; 
    self = [super initWithFrame:frame]; 
    if (self != nil) { 
     isa = prevClass; 
     [self foo]; // works now 
    } 
} 

这不是一个理想的解决办法,因为我每次我继承的时间来更新ISA,或者甚至有可能是不同的init方法我也必须更新。

1)理想情况下,有一种简单的方式来解决这个问题,仅仅通过代码?也许用我设置self =装载的笔尖的方式?

2)是否有适用于我想要做的更好的替代架构?一个例子是将所有者设置为self,但是您必须手动设置所有的property/outlet映射。

回答

0

而不是从子类本身内做到这一点,为什么不确保它是正确的类时,你第一次从类的外部实例吧:

SubClass *instance = [[SubClass alloc] initWithNibName:@"SomeClassView" bundle:nil]; 
3

交换ISA指针是一个问题,如果你的子类有实例变量除了在SomeClassView中声明的那些之外。请注意,您的nib文件有一个类型为SomeClassView的对象,这意味着,在加载nib文件时,nib加载器将分配该类型的对象并从nib文件解开它。由于nib加载程序分配的是一个SomeClassView对象,临时将isa指针更改为[SubViewClass class]本身不会使其成为SubViewClass类型的对象。

这么说,我不认为有使用含有对象,它们的类型需要在笔尖加载改变笔尖文件可靠和自动的方式。

你可以做的是让你的SomeClassView对象申报符合某些协议的委托。该协议将定义SomeClassView中可能被扩展的行为方法。例如,

@protocol SomeClassViewDelegate 
@optional 
- (void)someClassViewDidAwakeFromNib:(SomeClassView *)someClassView; 
@end 

相反子类SomeClassView的,你必须执行的任何自定义的行为,你目前在SubClassView任意对象。例如,

@interface SubClassViewBehaviour : NSObject <SomeClassViewDelegate> 
… 
@end 

@implementation SubClassViewBehaviour 
- (void)someClassViewDidAwakeFromNib:(SomeClassView *)someClassView { 
    // whatever behaviour is currently in -[SubClassView foo] 
} 
@end 

一个SubClassViewBehaviour对象将在代码中创建并设置为在加载笔尖文件笔尖文件的所有者,或与此有关的任何其他IB代理对象。SomeClassView将有一个委托插座连接到文件的所有者/代理对象,并且它会在适当的位置调用委托方法。例如,

@implementation SomeClassView 
- (void)awakeFromNib { 
    SEL didAwakeFromNib = @selector(someClassViewDidAwakeFromNib:); 
    if ([[self delegate] respondsToSelector:didAwakeFromNib]) { 
     [[self delegate] performSelector:didAwakeFromNib withObject:self]; 
    } 
} 
@end 

一个进一步的一句话:你的代码目前泄漏,因为被实例化两个对象视图对象:一个通过你的代码+alloc,并通过笔尖加载另一个。您将后者分配到self,因此通过+alloc创建的一个正在泄漏。另外,我相信您在第三个代码段中错过了拨打super的电话。

+0

谢谢,我更新了第三段代码。我认为我接受这样一个事实,即通过继承来改变笔尖类型是没有意义的。 – roblocop 2011-04-06 04:08:24

+0

@roblocop虽然它会很有用。我认为它保证提交一个改进请求雷达。 – 2011-04-06 04:16:49

+0

@Bavarious,你还是推荐这个实现,或者现在我们在2015年有更好的实现。 – 2015-09-22 13:17:33