[swift-users] Crasher in Swift<->Objective-C Interop!
Charles Srstka
cocoadev at charlessoft.com
Sun Apr 30 13:57:53 CDT 2017
Hello all,
I’ve discovered a bug that’s causing crashes when Swift code interoperates with AppKit in a certain way. I intend to file a bug report, but I’m not sure whether the bug should be filed as a Swift bug, or as an AppKit bug since a case could probably be made for either, and I thought I would ask the list first.
What’s happening, basically, is that when an NSTextFieldCell subclass contains Objective-C-compatible reference-type properties, and is loaded from a .nib file in which its containing text field has its Baseline aligned to some other object via autolayout, its properties get over-released when the cell is deallocated. A simple example that will cause the crash is:
@objc(Foo) class Foo: NSObject {}
@objc(CustomTextFieldCell) class CustomTextFieldCell: NSTextFieldCell {
let foo = Foo()
}
The equivalent code in Objective-C works properly and does not crash:
#import <Cocoa/Cocoa.h>
@interface Foo: NSObject
@end
@implementation Foo
@end
@interface CustomTextFieldCell: NSTextFieldCell
@property (nonatomic, strong) Foo *foo;
@end
@implementation CustomTextFieldCell
- (instancetype)initWithCoder:(NSCoder *)coder {
self->_foo = [Foo new];
return [super initWithCoder:coder];
}
@end
The problem seems to occur, as far as I can tell, because when the Baseline layout relation is applied, AppKit copies the text field’s cell. Subsequently, NSCell’s -copyWithZone: method calls NSCopyObject, which in turn calls a private function named “fixUpCopiedIvars.” With an Objective-C cell class, fixUpCopiedIvars calls class_getIvarLayout, and retains all its instance variables, so both the original cell and the copy have an owning reference to all of them. This retain is then balanced by a release when the cell is deallocated. With a Swift cell class, however, class_getIvarLayout returns NULL, so the ivars are never retained; however, this nonexistent retain is still balanced by a release when the cell is deallocated. The result is that the program accesses freed memory, leading to a crash or worse.
A sample project demonstrating all this is here: http://www.charlessoft.com/bug_examples/Crash_Swiftly.zip
So, there’s clearly a bug here, but I’m not sure which of these three possibilities is correct:
- This is a bug in AppKit, because NSCell should not be using the deprecated NSCopyObject or assuming that class_getIvarLayout will work.
- This is a bug in the Swift<->Objective-C bridge, because Swift can be used to write Objective-C objects, and legacy Objective-C code like NSCopyObject that interacts with said objects may assume that it can access instance variables via class_getIvarLayout; thus, the latter should work.
- Or option 3: The Swift team considers it to be a bug in AppKit, and the AppKit team considers this to be a bug in Swift, or maybe the Foundation team gets involved to make this into a triangle. This is obviously the worst-case scenario. ;-)
What does the community think? Should I file this as a bug on Swift? Foundation? AppKit? All three?
Thanks,
Charles
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170430/9b9eb6b1/attachment.html>
More information about the swift-users
mailing list