[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