覆盖 initWithCoder 时的无限循环

Posted

技术标签:

【中文标题】覆盖 initWithCoder 时的无限循环【英文标题】:Infinite loop when overriding initWithCoder 【发布时间】:2012-07-12 02:38:44 【问题描述】:

我有一个UIViewController,带有一些控制器和一些视图。其中两个视图(网格单元)是其他 nib。我有从网格单元到文件所有者的出口,但它们不会自动加载。

所以我尝试覆盖GridCell.minitWithCoder。 这将启动一个无限循环。

我知道可以覆盖initWithFrame 并从代码中添加子视图,但这不是我想要的。我希望能够在 Interface Builder 中移动视图并让 Xcode 使用正确的框架初始化视图。

我该如何实现这一目标?

编辑 1

我正试图在 Alexander 的帮助下让它工作。这就是我现在设置它的方式: MainView 具有 UIView,其自定义类设置为 GridCell。它在 MainView/File's Owner 中有一个出口。

从 GridCell.m 中删除所有初始化代码并为我的自定义类设置一个出口

虽然 MainView 仍然不显示 GridCell。没有错误,只是一个孤独的、空白的地方,红色开关应该在的地方。我做错了什么?

我非常接近以编程方式执行此操作。不过,我很想学习如何用笔尖做到这一点。

【问题讨论】:

[NSBundle loadNibNamed] 返回BOOL! 感谢您的回复。 NSBundle loadNibNamed 返回一个数组,我得到第一个对象:Apple developer 啊,好点子 - 我在看 OS X 参考 :) 【参考方案1】:

加载 nib 会导致再次调用 initWithCoder,因此您只想在子类当前没有任何子视图时这样做。

-(id)initWithCoder:(NSCoder *)aDecoder 
    self = [super initWithCoder:aDecoder];
    if (self) 
        if (self.subviews.count == 0) 
            UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil];
            UIView *subview = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
            subview.frame = self.bounds;
            [self addSubview:subview];
        
    
    return self;

【讨论】:

这个方法和ARC一致——也是唯一记得设置subview.frame = self.bounds的答案。 这可行,但它会在您的自定义视图下创建一个额外的UIView 难以置信。自从几周前开始使用 ios 以来,我一直在寻找这样的答案。你救了我。 这将导致子视图与self没有任何自动布局约束,因此如果self被调整大小,子视图将不会自动。 这个概念现在只是无限循环......然后崩溃了。【参考方案2】:

加载一个笔尖会导致一个对应的所有者在一个

  -(id) initWithCoder:(NSCoder *) coder;  

打电话

因此你在这个方法中的代码:

self = [[[NSBundle mainBundle] loadNibNamed: @"GridCell"
owner: self
options: nil] objectAtIndex:0];

将再次调用 initWithCoder 方法。那是因为您尝试再次加载笔尖。如果你定义一个自定义 UIView 并创建一个 nib 文件来布置它的子视图,你不能只是将一个 UIView 添加到另一个 nib 文件,将 IB 中的类名更改为你的自定义类并期望 nib 加载系统能够解决它。 你可以做的是:

您的自定义视图的 nib 文件需要将“文件所有者”类设置为您的自定义视图类,并且您需要在自定义类中有一个名为“toplevelSubView”的出口连接到您的自定义视图 nib 文件中的视图作为所有子视图的容器。向您的视图类添加额外的出口并将子视图连接到“文件的所有者”(您的自定义 UIView)。 (见https://***.com/a/7589220/925622)

编辑 好的,要回答您编辑的问题,我将执行以下操作:

转到您要在其中包含自定义视图的 nib 文件及其布局的 nib 文件。 不要进入自定义视图(GridCell)本身,而是创建一个包含您的网格单元格的视图(例如gridCellContainer,但它应该是一个UIView) 在自定义视图中自定义 initWithFrame 方法,就像在 initWithCoder 中所做的那样:

- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"GridCell" owner:self options:nil];    
        self = [nib objectAtIndex:0];
        self.frame = frame;
    
    return self;

然后,在 viewController 中,它是要包含自定义视图(带有 gridCellContainer 视图的视图)的视图的 fileOwner,在 viewDidLoad 中执行此操作,例如

//...
GridCell *gridCell = [[GridCell alloc] initWithFrame:self.viewGridCellContainer.bounds];
[self.viewGridCellContainer addSubview:gridCell];

现在一切都应该按您的预期工作

【讨论】:

感谢您的回复。在您的链接中,Robin 似乎像我一样在 initWithCoder 中加载了一个笔尖;但这就是导致无限循环的原因——这对我来说是个问题,但对他来说是个解决方案吗?我正在用您的解决方案更新我的问题,因为我仍然遇到一些麻烦。我可以在那里添加图片和东西.. 更新了我的问题。如果你有时间,请看一下。【参考方案3】:

文件的所有者不会接到电话

  -(id) initWithCoder:(NSCoder *) coder;  

加载 xib 时。

但是,该 xib 中定义的每个视图都会调用

-(id) initWithCoder:(NSCoder *) coder;  

加载 xib 时。

如果您在 xib 中定义了 UIView 的子类(即 GridCell),并且还尝试在子类的 initWithCoder 中加载相同的 xib,您将最终陷入无限循环。 但是,我看不出用例会是什么。

通常您在一个 xib 中设计 UIView 的子类(即 GridCell),然后在视图控制器的 xib 中使用该子类。

此外,您的自定义视图将在其 initWithCoder 中包含子视图的用例中看不到,即

-(id)initWithCoder:(NSCoder *)aDecoder 
    self = [super initWithCoder:aDecoder];
    if (self) 
        if (self.subviews.count == 0) 
            UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil];
            UIView *subview = [[nib instantiateWithOwner:self options:nil] objectAtIndex:0];
            subview.frame = self.bounds;
            [self addSubview:subview];
        
    
    return self;

除非您希望能够在其他一些 xib 中按需覆盖其视图层次结构。哪个 IMO 假定了一个外部依赖项(即在另一个 xib 中定义的层次结构)并且有点违背了首先拥有可重用 UIView 的目的。

请记住,在加载 xib 时,将实例作为文件的所有者传递,将设置其所有 IBOutlet(s)。在这种情况下,您将用 GridCell.xib 中的任何根视图替换 self(即 GridCell),从而在此过程中丢失所有 IBOutlet 连接。

- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"GridCell" owner:self options:nil];    
        self = [nib objectAtIndex:0];
        self.frame = frame;
    
    return self;

在“How to implement a reusable UIView.”上有一篇更详细的帖子,其中也有更详细的内容,希望能解决问题。

【讨论】:

【参考方案4】:

loadNibNamed:: 将调用 initWithCoder:

你为什么不遵循这个模式?

-(id)initWithCoder:(NSCoder *)coder


    if (self = [super initWithcoder:coder]) 

       // do stuff here ... 

    

    return self;                 

[super initWithcoder:coder] 会做你想避免的事情吗?

【讨论】:

【参考方案5】:

我在尝试覆盖 initWithsomething 方法时遇到了同样的问题,我们需要

-(id)initWithsomething:(Something *)something

    if (self = [super initWithsomething:something]) 
       // do stuff here ... 
    

    return self; 

改为

-(id)initWithsomething:(Something *)something

    if (self = [super init]) 
       // do stuff here ... 
    

    return self;

【讨论】:

这不是一个真正的问题,但你离得很远,所以我想帮助你。 super 指的是您要扩展的类:当头文件以 MyCustomLabel : UILabel 开头时,super 是 UIlabel。这就是为什么在初始化自己的对象时,还需要使用 [super init] 来初始化 super。你不应该在 MyCustomLabel 中使用 initWithsomething 并调用 super 的 initWithsomething,因为你会用你自己的方法覆盖 super 的方法,因为它是同名的。也就是说,您的方法不应称为 initWithCoder 或 initWithFrame。想出新的东西

以上是关于覆盖 initWithCoder 时的无限循环的主要内容,如果未能解决你的问题,请参考以下文章

在c ++ 11中使用异常时的无限循环

当覆盖 initWithCoder 时,总是需要调用 [super initWithCoder: coder]

确定文件大小时的Python无限循环

为啥我的代码在执行时的初始嵌套 for 循环中进入无限循环?

从文件读取数据时的无限循环

构造链表时的无限循环