objective-cmacosbluetoothautomatic-ref-countingiobluetooth

Segmentation fault when flag `-fobjc-arc` is used


I am using the IOBluetooth framework on Mac OSX to do device discovery. I have added some NSLog() inside the callback functions, so I know the progress.

If I compile the code with gcc on command line like this, everything works fine (source file f.m, output a):

gcc -o a f.m -framework Foundation -framework IOBluetooth

However, if I add a -fobjc-arc flag to ensure Automatic Reference Counting:

gcc -fobjc-arc -o a f.m -framework Foundation -framework IOBluetooth

Compilation is still ok, but executing the file ./a resulting in either a seg fault:

2017-06-21 22:06:23.150 a[718:17437] Program started ...
Segmentation fault: 11

or hanging there forever:

2017-06-21 22:06:27.070 a[721:17809] Program started ...

Sometimes it hangs, sometimes it seg faults. The behavior is inconsistent. If I don't add the -fobjc-arc flag, everything works as expected (on the surface at least).

So my question is, why does it behave the way it does after I add the -fobjc-arc flag?

In case it helps, the complete source file is here:

#import <IOBluetooth/IOBluetooth.h>

@interface InquiryDelegate : NSObject <IOBluetoothDeviceInquiryDelegate>
@end

@implementation InquiryDelegate
-(void)deviceInquiryStarted:(IOBluetoothDeviceInquiry *)sender
{
    NSLog(@"Inquiry started ...");
}

-(void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *)sender
                         device:(IOBluetoothDevice *)device
{
    NSLog(@"Device found");
}

-(void)deviceInquiryComplete:(IOBluetoothDeviceInquiry *)sender
                       error:(IOReturn)error
                     aborted:(BOOL)aborted
{
    NSLog(@"Inquiry complete");
}

-(void)deviceInquiryUpdatingDeviceNamesStarted:(IOBluetoothDeviceInquiry *)sender
                              devicesRemaining:(uint32_t)devicesRemaining
{
}

-(void)deviceInquiryDeviceNameUpdated:(IOBluetoothDeviceInquiry *)sender
                               device:(IOBluetoothDevice *)device
                     devicesRemaining:(uint32_t)devicesRemaining
{
}
@end

int main(int argc, const char* argv[]) {
    @autoreleasepool {
        NSLog(@"Program started ...");

        IOBluetoothDeviceInquiry* di = [[IOBluetoothDeviceInquiry alloc]
                                           initWithDelegate:[[InquiryDelegate alloc] init]];
        [di start];

        [[NSRunLoop currentRunLoop] run];
    }
}

In case anyone is wondering, my goal is to produce a dynamic library for use in a JNI project, with no GUI involved. This is my attempt to gain some experience with the way Bluetooth is done on Mac, and get myself acquainted with Objective-C. I am coming from a Linux background, so prefer to stay on the command line if possible.

Thanks in advance.


Solution

  • Thanks to hints given by commenters above, I am able to solve the problem by adding a strong reference to the delegate object:

    InquiryDelegate* g = [[InquiryDelegate alloc] init];
    
    IOBluetoothDeviceInquiry* di = [[IOBluetoothDeviceInquiry alloc]
                                       initWithDelegate:g];
    

    Thanks to both @Willeke and @Ssswift.