0

I have an Objective-C class, SBUSBDevice, which represents a USB drive that the user has plugged into their system. In one of my view controllers, I grab the dictionary containing these objects, add them to an array, and attempt to do things with this array:

#import "SBUSBSetupWindowController.h"
#import "SBAppDelegate.h"
#import "SBUSBDevice.h"

@interface SBUSBSetupWindowController ()

@property (strong) NSDictionary *usbDictionary;
@property (weak) IBOutlet NSTableView *tableView;
@property (weak) IBOutlet NSButton *enableStartupDiskButton;

@property (strong) NSMutableArray *usbArray;

@end

@implementation SBUSBSetupWindowController

- (id)initWithWindow:(NSWindow *)window {
self = [super initWithWindow:window];
if (self) {
    // Initialization code here.
    self.usbDictionary = [[(SBAppDelegate *)[NSApp delegate] usbDictionary] copy];

    self.usbArray = [[NSMutableArray alloc] initWithCapacity:[self.usbDictionary count]];
    [self.usbDictionary enumerateKeysAndObjectsUsingBlock:^(id key, SBUSBDevice *object, BOOL *stop) {
        NSLog(@"%@ = %@", key, object);
        [self.usbArray addObject:object]; // <-- CRASH OCCURS HERE
    }];
}
    return self;
}

Unfortunately, when I run the code, it throws the following exception:

[SBUSBDevice copyWithZone:]: unrecognized selector sent to instance 0x6080004602c0

As far as I know, I am not trying to copy any SBUSBDevice instances, so there should be not need to even execute copyWithZone:. I am using this same technique in other areas in my app, with slight differences, and they all work. Why is this exception being thrown?

Here is how the SBUSBDevice objects are originally created. Note that they are being put into the dictionary which is later referenced by the code above:

- (void)detectAndSetupUSBs {
if (!self.usbDictionary) {
    self.usbDictionary = [[NSMutableDictionary alloc] initWithCapacity:10];
}

NSArray *volumes = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:0];
BOOL isRemovable, isWritable, isUnmountable;
NSString *description, *volumeType;

BOOL acceptHFSDrives = [[NSUserDefaults standardUserDefaults] boolForKey:@"AcceptHFSDrives"];

for (NSURL *mountURL in volumes) {
    NSString *usbDeviceMountPoint = [mountURL path];
    if ([[NSWorkspace sharedWorkspace] getFileSystemInfoForPath:usbDeviceMountPoint isRemovable:&isRemovable isWritable:&isWritable isUnmountable:&isUnmountable description:&description type:&volumeType]) {
        if (isRemovable && isWritable && isUnmountable) {
            NSLog(@"Detected eligible volume at %@. Type: %@", usbDeviceMountPoint, volumeType);

            if ([usbDeviceMountPoint isEqualToString:@"/"]) {
                // Don't include the root partition in the list of USBs.
                continue;
            }
            else {
                if ([volumeType isEqualToString:@"msdos"] ||
                    ([volumeType isEqualToString:@"hfs"] && acceptHFSDrives)) {
                    SBUSBDevice *usbDevice = [[SBUSBDevice alloc] init];
                    usbDevice.path = usbDeviceMountPoint;
                    usbDevice.name = [usbDeviceMountPoint lastPathComponent];
                    [SBAppDelegate uuidForDeviceName:usbDeviceMountPoint];

                    self.usbDictionary[usbDevice.name] = usbDevice;
                }
            }
        }
    }
}
}

And here's the text of the exception (it occurs once for every USB drive that is plugged in):

2014-09-17 15:59:50.586 Mac Linux USB Loader[875:303] -[SBUSBDevice copyWithZone:]: unrecognized selector sent to instance 0x608000469140
2014-09-17 15:59:50.596 Mac Linux USB Loader[875:303] (
0   CoreFoundation                      0x00007fff8de8725c __exceptionPreprocess + 172
1   libobjc.A.dylib                     0x00007fff85e2ae75 objc_exception_throw + 43
2   CoreFoundation                      0x00007fff8de8a12d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3   CoreFoundation                      0x00007fff8dde5272 ___forwarding___ + 1010
4   CoreFoundation                      0x00007fff8dde4df8 _CF_forwarding_prep_0 + 120
5   AppKit                              0x00007fff8aa79186 -[NSCell _setContents:] + 74
6   AppKit                              0x00007fff8aa95f5c -[NSCell setObjectValue:] + 317
7   AppKit                              0x00007fff8aa95c8e -[NSTextFieldCell setObjectValue:] + 91
8   AppKit                              0x00007fff8ad13df6 -[NSTableView preparedCellAtColumn:row:] + 589
9   AppKit                              0x00007fff8ad13a5e -[NSTableView _drawContentsAtRow:column:withCellFrame:] + 44
10  AppKit                              0x00007fff8ad13793 -[NSTableView drawRow:clipRect:] + 1629
11  AppKit                              0x00007fff8ad12fed -[NSTableView drawRowIndexes:clipRect:] + 776
12  AppKit                              0x00007fff8abdc331 -[NSTableView drawRect:] + 1484
13  AppKit                              0x00007fff8abb7557 -[NSView(NSInternal) _recursive:displayRectIgnoringOpacity:inGraphicsContext:CGContext:topView:shouldChangeFontReferenceColor:] + 1082
14  AppKit                              0x00007fff8abb700d __46-[NSView(NSLayerKitGlue) drawLayer:inContext:]_block_invoke + 186
15  AppKit                              0x00007fff8abb6e03 -[NSView(NSLayerKitGlue) _drawViewBackingLayer:inContext:drawingHandler:] + 2297
16  AppKit                              0x00007fff8abb64f8 -[NSView(NSLayerKitGlue) drawLayer:inContext:] + 108
17  QuartzCore                          0x00007fff8cb46812 CABackingStoreUpdate_ + 2220
18  QuartzCore                          0x00007fff8cb45f60 ___ZN2CA5Layer8display_Ev_block_invoke + 59
19  QuartzCore                          0x00007fff8cb45f1c x_blame_allocations + 84
20  QuartzCore                          0x00007fff8cb45a2b _ZN2CA5Layer8display_Ev + 1539
21  AppKit                              0x00007fff8abb63c3 _NSBackingLayerDisplay + 235
22  AppKit                              0x00007fff8ab8d74b -[_NSViewBackingLayer display] + 811
23  QuartzCore                          0x00007fff8cb45162 _ZN2CA5Layer17display_if_neededEPNS_11TransactionE + 590
24  QuartzCore                          0x00007fff8cb448b1 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 35
25  QuartzCore                          0x00007fff8cb4433c _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 236
26  QuartzCore                          0x00007fff8cb43fd6 _ZN2CA11Transaction6commitEv + 388
27  QuartzCore                          0x00007fff8cb54761 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 71
28  CoreFoundation                      0x00007fff8ddb7d67 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
29  CoreFoundation                      0x00007fff8ddb7cd7 __CFRunLoopDoObservers + 391
30  CoreFoundation                      0x00007fff8dda8e94 CFRunLoopRunSpecific + 340
31  HIToolbox                           0x00007fff8b725a0d RunCurrentEventLoopInMode + 226
32  HIToolbox                           0x00007fff8b7257b7 ReceiveNextEventCommon + 479
33  HIToolbox                           0x00007fff8b7255bc _BlockUntilNextEventMatchingListInModeWithFilter + 65
34  AppKit                              0x00007fff8aa5624e _DPSNextEvent + 1434
35  AppKit                              0x00007fff8aa5589b -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
36  AppKit                              0x00007fff8aa4999c -[NSApplication run] + 553
37  AppKit                              0x00007fff8aa34783 NSApplicationMain + 940
38  Mac Linux USB Loader                0x000000010001c432 main + 34
39  libdyld.dylib                       0x00007fff83b445fd start + 1
)

I've tried everything that I've thought of, however, I am largely self-taught at Objective-C and I am afraid that I may be missing something. If you need any additional source code, please feel free to ask.

6
  • If you look at the exception stack trace it will tell you where this is occurring. If you had provided us with the exception stack trace (as you're supposed to) we would know that too. Commented Sep 13, 2014 at 18:32
  • @HotLicks I'll include the stack trace as soon as I can, but I can't test the program now. I'll edit the question and include it ASAP. Commented Sep 13, 2014 at 21:50
  • @HotLicks I've edited the question to include the stack trace. Sorry for the long delay. Commented Sep 17, 2014 at 20:03
  • You are referencing your usbArray in cellForRowAtIndexPath to populate the cell, and you're sticking a SBUSBDevice pointer directly into the cell display structures somewhere. Probably you meant to use some property (eg, an NSString) of the SBUSBDevice object. Commented Sep 17, 2014 at 20:11
  • @HotLicks That's it, thanks! :) Please include it as an answer so I can give you rep. Commented Sep 17, 2014 at 20:24

3 Answers 3

1

You are referencing your usbArray in cellForRowAtIndexPath to populate the cell, and you're sticking a SBUSBDevice pointer directly into the cell display structures somewhere. Probably you meant to use some property (eg, an NSString) of the SBUSBDevice object.

Sign up to request clarification or add additional context in comments.

Comments

0

I think I understand what is happening.

The copy message you send to the usbDictionary sends a copy message to each of it's content (this is typical to almost all Collection items).

Therefore, the copy message is sent to SBUSBDevice objects (which are stored in the dictionary) and thus, causing the exception.

Looking at the pieces of code attached, I can't think of the reason why we're making a copy here. Adding objects to collections should automatically increase retain count.

3 Comments

The crash occurs even without the copy statement. I added it in there after I got the error because I thought having a separate copy of the data might resolve the issue.
Without the copy - what is the exception you get? Without that it might be hard to guess. First, I'd recommend using a weak reference in the block. So add a line __weak blockSelf = self and use this blockSelf inside the block.
Without the copy, I get the exception that I just posted in the question.
0

I am relatively sure it is this line:

self.usbDictionary = [[(SBAppDelegate *)[NSApp delegate] usbDictionary] copy];

If you try to copy a dictionary containing objects that do not implement the copying protocols, it will crash. Are you certain that the crash happens where you think it does, and not on this line?

Reference Documentation for the NSCopying Protocol

2 Comments

The crash occurs even without the copy statement. I added it in there after I got the error because I thought having a separate copy of the data might resolve the issue.
Commenting out the line that I pointed out causes the error to go away, so it must be this line that is problematic. Trouble is, without it, the problem doesn't work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.