Swift NSCopying

Dear All,

I'm trying to implement NSCopying for a NSTextFieldCell based class. But I'm getting crashes all the time, because there is an array in the new class. Here is the code:


class myTextFieldCell: NSTextFieldCell { 
     var thearray: [Int] = [] 

     override func copy(with zone: NSZone?) -> Any { 
          let copy = super.copy(with: zone) as! myTextFieldCell 
          copy.thearray = [] 
          return copy 
     } 
}


It crashes all the time, no matter how the NSCopying is implemented, and I've tried all the options I can think of:

1. Copy the arrray as it is - copy.thearray = self.thearray

2. Initialize the array (as shown in the code above)


Any help on how to implement NSCopying in this situation will be highly appreciated.


I. Nikolov

Accepted Reply

Crashes how? And when?


It would be helpful if you could include a few entries from the top of the backtrace at the time of the crash.


Creating a copy by invoking super.copy is very risky, because you don't know what it does with the storage for your instance variables. This is complicated by the fact that an array property wraps an object reference, but you don't know exactly how Swift is handling this.


In particular, if the instance variable storage for you array contains trash or all zero bits after invoking super.copy, assigning to it may well crash because the old value is invalid. (The assignment would "fix" it, but you might not get that far.)


Or, more subtly, an object reference might be copied but not retained, leading to an overrelease when you make the array assignment.


If there's any way you can proceed without subclassing NSTextFieldCell at all, your code will be safer. NSCells are pretty much obsolete these days, at least for new 3rd-party development.

Replies

Crashes how? And when?


It would be helpful if you could include a few entries from the top of the backtrace at the time of the crash.


Creating a copy by invoking super.copy is very risky, because you don't know what it does with the storage for your instance variables. This is complicated by the fact that an array property wraps an object reference, but you don't know exactly how Swift is handling this.


In particular, if the instance variable storage for you array contains trash or all zero bits after invoking super.copy, assigning to it may well crash because the old value is invalid. (The assignment would "fix" it, but you might not get that far.)


Or, more subtly, an object reference might be copied but not retained, leading to an overrelease when you make the array assignment.


If there's any way you can proceed without subclassing NSTextFieldCell at all, your code will be safer. NSCells are pretty much obsolete these days, at least for new 3rd-party development.

There are no object references in the array. It's an [Int] array. It crashes ... well ... very strangely. I can make an example project and upload it at github, just say so. And regarding the NSCells - unfortunately cell-based tables are much faster compared to view based. At least according to my tests.


I. Nikolov

On second thought - your answer is EXTREMELY correct. Yes - you are right ... 'Creating a copy by invoking super.copy is very risky .....'

I figured out what and how to do - It took me just a minute to realize what actually I'm doing ... I guess I need to rest a little bit ;-) - too much coding ;-)


Thank you very much.

Historically there was a weird edge case with subclassing NSCell. Specifically, NSCell’s

-copyWithZone:
would copy itself by calling
NSCopyObject
(yetch!), which causes all sorts of pain. You can read Implementing NSCopying for some background on this.

According to Transitioning to ARC Release Notes this is not an issue with ARC-based Objective-C code, but I could imagine that Swift might trip over this pitfall as well. Alas, I don’t have time to research the gory details.

I’d appreciate you filing a bug with a small example that illustrates the crash; that way the Swift engineering team can look into this. Please post your bug number, just for the record

As to a workaround, if you need one pronto then an easy option would be to implement your NSCell in Objective-C ARC, where this isn’t an issue. If you want to continue writing the bulk of your code in Swift, you could have your Objective-C cell maintain a reference to a Swift object and bounce the relevant methods over there.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thank you for the answer. I'll prepare a small project and will fire a bug. Will paste the ID here.


I. Nikolov