【问题标题】:Copying NSTextFieldCell subclass in Swift causes crash在 Swift 中复制 NSTextFieldCell 子类会导致崩溃
【发布时间】:2016-01-07 09:11:57
【问题描述】:

我一直在尝试创建 NSTextFieldCell 的子类以与自定义 NSTextField(使用 Swift)一起使用。但是,尝试复制子类单元格时,我的代码会中断。我的基本代码是

class XYTextFieldCell: NSTextFieldCell {
    var borderColor = NSColor.init(red: 0.5, green: 0.5, blue: 0.5, alpha: 1)        
    override init(imageCell image: NSImage?) {
        super.init(imageCell: image)
    }

    override init(textCell aString: String) {
        super.init(textCell: aString)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    deinit {
        Swift.print("Deinit XYTextFieldCell: \(unsafeAddressOf(self))")
    }
}

在 AppDelegate 中(尝试在一个小应用程序中模拟崩溃),我有

func applicationDidFinishLaunching(aNotification: NSNotification) {
    let textFieldCell = XYTextFieldCell.init(textCell: "Test")
    Swift.print("TextFieldCell: \(unsafeAddressOf(textFieldCell))")
    print("textFieldCell.color: \(unsafeAddressOf(textFieldCell.borderColor))")
    copyTextFieldCell(textFieldCell)
}

func copyTextFieldCell(textFieldCell: XYTextFieldCell) {
    Swift.print("TextFieldCell (param): \(unsafeAddressOf(textFieldCell))")
    let copy = textFieldCell.copy() as! XYTextFieldCell
    Swift.print("TextFieldCell (copy): \(unsafeAddressOf(copy))")
    print("copy.color: \(unsafeAddressOf(copy.borderColor))")
}

应用程序因

而崩溃
[NSColorSpaceColor release]: message sent to deallocated instance 0x600000075240

完整的输出是

TextFieldCell: 0x00006080000a61e0 
textFieldCell.color: 0x0000608000074840 
TextFieldCell (param): 0x00006080000a61e0 
TextFieldCell (copy): 0x00006080000a62a0 
copy.color: 0x0000608000074840 
Deinit XYTextFieldCell: 0x00006080000a62a0 
Deinit XYTextFieldCell: 0x00006080000a61e0 
2015-10-09 16:52:35.043 Test[86949:4746488] *** -[NSColorSpaceColor release]: message sent to deallocated instance 0x608000074840

复制后似乎没有正确保留borderColor(并且被双重释放)。然后我尝试添加一个复制重载来尝试强制复制borderColor。

override func copyWithZone(zone: NSZone) -> AnyObject {
        let myCopy = super.copyWithZone(zone) as! XYTextFieldCell
        myCopy.borderColor = borderColor.copyWithZone(zone) as! NSColor

        return myCopy
    }

但是,它仍然会因同样的错误而崩溃

TextFieldCell: 0x00006080000ab4c0 textFieldCell.color: 0x00006080000769c0    
TextFieldCell (param): 0x00006080000ab4c0 
TextFieldCell (copy): 0x00006080000ab520 
copy.color: 0x00006080000769c0 
Deinit XYTextFieldCell: 0x00006080000ab520 
Deinit XYTextFieldCell: 0x00006080000ab4c0 
2015-10-09 16:54:54.248 Test[87031:4749016] *** -[NSColorSpaceColor release]: message sent to deallocated instance 0x6080000769c0

我可以通过在 copyWithZone 中初始化一个新的 XYTextFieldCell 来避免崩溃:(而不是调用 super.copyWithZone)。但是,这意味着我也必须手动将所有超类定义的属性重新分配给我的副本。

有没有办法正确复制 NSTextFieldCell,这样它就不会双重释放我的子类属性。从 NSButtonCell 子类化时,我也注意到了这种行为。但是,如果我不从任何一个继承(XYTextFieldCell 是一个根 Swift 类),那么它工作正常。谢谢

【问题讨论】:

    标签: swift cocoa nstextfieldcell


    【解决方案1】:

    我知道这是一个老问题,但我想我会回答的。您需要为复制的对象添加保留。这是我为可选的 NSColor 做的事情:

    class TableViewTextFieldCell: NSTextFieldCell {
    
        private var previousTextColor: NSColor?
    
        // Super newbie mistake of forgetting to implement copyWithZone; why would we need it if we aren't using a cell-based NSTableView?  This shouldn't be needed..but apparently NSTextFieldCell's baseline measurement with autolayout does a copy! who would have guessed. NSCell's implementation does a NSCopyObject, which doesn't retain ivars
        override func copy(with zone: NSZone? = nil) -> Any {
            let result: TableViewTextFieldCell = super .copy(with: zone) as! TableViewTextFieldCell
            if let previousTextColor = result.previousTextColor {
                // Add the needed retain now
                let _ = Unmanaged<NSColor>.passRetained(previousTextColor)
            }
            return result
        }
    }
    

    【讨论】:

      【解决方案2】:

      看起来我已被 NSCopyObject() 捕获,详细信息 here。该链接的要点是 NSCell 子类使用 NSCopyObject() 来符合 NSCopying 协议 - 并且 NSCopyObject() 似乎没有正确设置保留计数。

      在 Objective-C 中概述的解决方案,我不知道如何将其转换为 Swift(因为它涉及在 copyWithZone: 方法中直接访问 Objective-C 的 ivars)。所以,我必须在objective-c 中实现基本子类(并在objective-c 中进行复制)。然后我将它连接到一个 swift 子类

      RMTextFieldCell.h(在objective-c中)

      #import <Cocoa/Cocoa.h>
      
      @interface RMTextFieldCell : NSTextFieldCell
      
      @property (nonatomic, strong) NSColor* borderColor;
      
      @end
      

      RMTextFieldCell.m(在objective-c中)

      #import "RMTextFieldCell.h"
      
      @implementation RMTextFieldCell
      
      -(id) initTextCell:(NSString *)aString
      {
          self = [super initTextCell:aString];
          [self setup];
      
          return self;
      }
      
      -(id) initImageCell:(NSImage *)image
      {
          self = [super initImageCell:image];
          [self setup];
      
          return self;
      }
      
      -(id) initWithCoder:(NSCoder *)aDecoder
      {
          self = [super initWithCoder:aDecoder];
          [self setup];
      
          return self;
      }
      
      -(RMTextFieldCell*) copyWithZone:(NSZone*) zone
      {
          RMTextFieldCell* copy = [super copyWithZone:zone];
          copy->_borderColor = nil;
          copy.borderColor = self.borderColor;
      
          return copy;
      }
      
      -(void) setup
      {
          self.borderColor = [NSColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:1.0];
      }
      
      @end
      

      RMSwiftTextFieldCell.swift(快速)

      import Cocoa
      
      class RMSwiftTextFieldCell: RMTextFieldCell {
      
      
          override init(imageCell image: NSImage?) {
              super.init(imageCell: image)
          }
      
          override init(textCell aString: String) {
              super.init(textCell: aString)
          }
      
          required init?(coder aDecoder: NSCoder) {
              super.init(coder: aDecoder)
          }
      
          deinit {
              Swift.print("Deinit RMTextFieldCell: \(unsafeAddressOf(self))")
          }
      }
      

      这有点令人费解,但似乎在我的初始测试中有效。如果有人有更好的解决方案,我将不胜感激:) 谢谢

      编辑:看起来我什至不需要在objective-c子类中实现copyWithZone:。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-08
        • 1970-01-01
        • 1970-01-01
        • 2016-11-29
        相关资源
        最近更新 更多