【问题标题】:Subclass of NSButton: call of action method crashesNSButton 的子类:调用操作方法崩溃
【发布时间】:2012-07-28 23:33:42
【问题描述】:

在我的应用程序中有一个NSWindowController 的子类。在- (void)awakeFromNib 中,我创建了一个NSView 子类的实例来创建底部放置的按钮栏:

self.buttonBar = [[CNButtonBar alloc] initWithSize:NSMakeSize(tableViewRect.size.width-1, 25)];

CNButtonBar 这个东西有一个方法可以给自己添加按钮。我创建了两个:

[buttonBar addButtonWithTitle:nil
                        image:[NSImage imageNamed:NSImageNameAddTemplate]
                       target:self
                       action:@selector(btnAccountAddAction:)
                    alignment:CNBarButtonAlignLeft];

[buttonBar addButtonWithTitle:nil
                        image:[NSImage imageNamed:NSImageNameRemoveTemplate]
                       target:self
                       action:@selector(btnAccountRemoveAction:)
                    alignment:CNBarButtonAlignLeft];

这两个操作方法在NSViewController 的同一个实例中定义,我在其中发送两个addButtonWithTitle:... 消息。

用于设置动作的方法定义如下:

- (void)btnAccountAddAction:(id)sender
{
    ....
}

- (void)btnAccountRemoveAction:(id)sender
{
    ....
}

但是,它不起作用。每次如果我单击这两个按钮之一,我的应用程序就会崩溃并抛出如下异常:

-[__NSArrayM btnAccountAddAction:]: unrecognized selector sent to instance 0x1001454e0

检查过这个异常是绝对正确的。 NSArray 没有我想调用的这种方法。但是,有时抛出此异常的对象会发生变化。有时有__NSData 对象,有时有__NSDictionary 等等。似乎接收者不是我的NSWindowController 子类。但是给定的实例 ID 0x1001454e0 与我的 NSWindowController 相同。

这让我想把头发扯下来!这里出了什么问题……? 谢谢。


更新

我不得不说,CNButtonBarCNButtonBarButton 都是完全由代码绘制的。 addButtonWithTitle:image:target:action:alignment:的完整方法在这里(记住,这发生在CNButtonBarNSView的子类(我应该用NSViewController代替它吗?),CNButtonBarButtonNSButton的子类):

- (void)addButtonWithTitle:(NSString*)title image:(NSImage*)image target:(id)target action:(SEL)action alignment:(CNBarButtonAlign)align
{
    if (self.buttons == nil)
        self.buttons = [[NSMutableArray alloc] init];

    CNButtonBarButton *button = [[CNButtonBarButton alloc] init];
    [self.buttons addObject:button];

    button.title = title;
    button.image = image;
    button.target = target;
    button.action = action;
    button.align = align;

    NSRect buttonRect;
    NSSize titleSize = NSMakeSize(0, 0);
    if (button.title.length > 0) {
        NSMutableParagraphStyle* textStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
        [textStyle setAlignment: NSCenterTextAlignment];

        NSColor *textColor = [[NSColor blackColor] colorWithAlphaComponent:0.9];
        NSShadow* textShadow = [[NSShadow alloc] init];
        [textShadow setShadowColor: [NSColor whiteColor]];
        [textShadow setShadowOffset: NSMakeSize(0, -1)];
        [textShadow setShadowBlurRadius: 0];

        button.titleTextAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [NSFont fontWithName:@"Helvetica Neue" size:11.0], NSFontAttributeName,
                                      textShadow,                     NSShadowAttributeName,
                                      textColor,                      NSForegroundColorAttributeName,
                                      textStyle,                      NSParagraphStyleAttributeName,
                                      nil];
        titleSize = [title sizeWithAttributes:button.titleTextAttributes];
        buttonRect = NSMakeRect(0, 0, roundf(titleSize.width) + kTextInset * 2, NSHeight(self.frame) - 1);

    }

    if (button.image != nil) {
        NSSize imageSize = [image size];
        if (button.title.length == 0) {
            buttonRect = NSMakeRect(0, 0, imageSize.width + kImageInset * 2, NSHeight(self.frame) - 1);

        } else {
            buttonRect = NSMakeRect(0, 0,
                                    (imageSize.width + kImageInset * 2) + (roundf(titleSize.width) + kTextInset),
                                    NSHeight(self.frame) - 1);
        }
    }

    switch (self.align) {
        case CNButtonBarAlignNormal: {
            switch (button.align) {
                case CNButtonBarButtonAlignLeft: {
                    button.frame = NSMakeRect(offsetLeft, 0, NSWidth(buttonRect), NSHeight(buttonRect));
                    offsetLeft += 1 * [self.buttons indexOfObject:button] + NSWidth(button.frame);
                    break;
                }

                case CNButtonBarButtonAlignRight: {
                    button.frame = NSMakeRect(offsetRight - NSWidth(buttonRect), 0, NSWidth(buttonRect), NSHeight(buttonRect));
                    offsetRight -= 1 * [self.buttons indexOfObject:button] + NSWidth(button.frame);
                    break;
                }
            }
            break;
        }

        case CNButtonBarAlignCentered: {
            break;
        }
    }
    [self addSubview:button];
}

更新 2

问题解决了。经过一些调试,我发现这一定是 ARC 自动保留/释放的问题。我是对的。但问题是我的NSViewController 子类。由于将实例作为局部变量(没有强指针)而泄漏。感谢Sean D.,它为我指明了正确的方向。 (-;

【问题讨论】:

  • 您可能应该显示以下代码:btnAccountAddAction
  • 问题很可能出在CNButtonBar,但如果没有来自addButtonWithTitle: 的一些代码或信息,我们将无法诊断。
  • “我创建了一个 NSView 子类的实例来创建一个底部放置的按钮栏”——这听起来像是一些 other 类,而不是窗口控制器,正在创建 CNButtonBar实例。如果是这种情况,则其他类将自己设置为目标,但选择器方法由 win ctrllr 持有。这将解释“无法识别的选择器”异常 - 但不是被调用类的奇怪分类。
  • @HannesSverrisson 这些方法实际上是空的。只有一条日志消息。我给你一个更新。

标签: objective-c action target nsbutton nswindowcontroller


【解决方案1】:

看起来正在发生两件事中的一件。第一种可能性是您的 CNButtonBar 对象在按钮仍然存在时被释放。如果按钮仍然存在,他们会尝试将这些选择器发送到任何碰巧占用 CNButtonBar 曾经所在的内存的地方。我会说确保它不会在任何地方自动释放,并且你不会不小心在窗口控制器的 -awakeFromNib 方法中自动释放它。 (如果你和我一样,那很可能是罪魁祸首。)

第二种可能性是您的-addButtonWithTitle:image:target:action:alignment: 方法正在设置按钮的操作而不是它们的目标。确保您对该方法的实现调用按钮上的-setTarget:-setAction:

【讨论】:

  • 其实我用的是ARC。所以我认为CNButtonBar 已经存在了。但我必须再次测试它。目标和操作都已设置,而不是 nil
  • 您是否在某处保留了指向它的强指针?从您上面发布的代码来看,您似乎只是将其创建为局部变量,在这种情况下,ARC 将在创建它的方法结束时自动释放它。
  • 是的。这个例子是错误的,我现在已经修复了。这个按钮栏是一个带有强指针的属性。
  • 很高兴看到您解决了问题!如果我的回答有帮助,您介意将其标记为已接受吗?谢谢! :-)
猜你喜欢
  • 2015-12-29
  • 2016-07-05
  • 2015-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多