我一直在尝试创建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类),那么它可以正常工作。感谢
答案 0 :(得分:2)
我知道这是一个老问题,但我想我会回答。您需要为复制的对象添加保留。这是我为可选的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
}
}
答案 1 :(得分:1)
看起来我被NSCopyObject()捕获为详细的here。链接的要点是NSCell子类使用NSCopyObject()符合NSCopying协议 - 而NSCopyObject()似乎没有正确设置保留计数。
其中概述的解决方案在Objective-C中,我不知道如何将其转换为Swift(因为它涉及直接访问copyWithZone:方法中的Objective-C ivars)。所以,我必须在objective-c中实现基本子类(并在objective-c中执行复制)。然后我将其桥接到一个快速的子类
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(在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: