Xamarin.iOS SIGSEGV在尝试发现连接的BLE外设的服务时

时间:2015-04-21 22:05:49

标签: xamarin xamarin.ios core-bluetooth xamarin-studio

我正在Xamarin制作一个BLE跨平台应用程序,但未能发现连接外围设备的服务。

以下是共享项目中的精简代码,只是为了能够重现此问题;

internal class BleTransport
{
    //Private stuff
    private CBCentralManager Central = null;
    private CBPeripheral CurrentPeripheral = null;
    private bool IsScanning = false;

    internal BleTransport ()
    {
        //Constructor -- initialize everything that needs to be initialized!
        Central = new CBCentralManager (DispatchQueue.MainQueue);

        //Setup delegates to the central's events
        Central.UpdatedState += (object sender, EventArgs e) => CentralStateChanged.Set ();
        Central.DiscoveredPeripheral += (object sender, CBDiscoveredPeripheralEventArgs e) => DiscoveredPeripheral (e.Peripheral, e.RSSI, e.AdvertisementData);             
        Central.ConnectedPeripheral += (object sender, CBPeripheralEventArgs e) => ConnectedPeripheral (e.Peripheral);
        Central.FailedToConnectPeripheral += (object sender, CBPeripheralErrorEventArgs e) => FailedToConnectPeripheral (e.Error, e.Peripheral);
        Central.DisconnectedPeripheral += (object sender, CBPeripheralErrorEventArgs e) => DisconnectedPeripheral (e.Error, e.Peripheral);
    }

    readonly AutoResetEvent CentralStateChanged = new AutoResetEvent (false);
    private async Task<bool> WaitForCentralState (CBCentralManagerState state, TimeSpan timespan)
    {
        Console.WriteLine("Waiting for state : " + state);

        //TODO; Keep track of time and decrease time-span accordingly
        bool timedout = false;

        while (!timedout && Central.State != state) 
        {
            timedout = await Task.Run (delegate {
                //WaitOne returns false if we timeout
                return !CentralStateChanged.WaitOne (timespan);
            });
        }

        //Return true if we made it, amd false if we timedout
        return !timedout;
    }

    private void StartScanning()
    {
        Console.WriteLine("Start scan requestd!");

        if (!IsScanning) 
        {
            //Start!
            IsScanning = true;

            Console.WriteLine("Kicking scan for peripherals!");
            CBUUID[] uuids = null;
            Central.ScanForPeripherals (uuids);
        }
    }

    private void StopScanning()
    {
        Console.WriteLine("Stop scan requested!");

        if (IsScanning) 
        {
            //Stop!
            IsScanning = false;
            Central.StopScan();
        }
    }

    public async Task<bool> SetupTransport ()
    {
        Console.WriteLine("Setting up transport!");

        //Wait for state update...
        if (!await WaitForCentralState (CBCentralManagerState.PoweredOn, TimeSpan.FromSeconds (2))) {

            //Failed detecting a powered-on BLE interface
            Console.WriteLine("Failed detecting usable BLE interface!");
            return false;
        }

        //Kick scanning... 
        StartScanning();

        //Wait for discovery and connection to a valid peripheral, or timeout trying...
        await Task.Delay(10000);

        return true;
    }

    #region Internal BLE utility methods

    private void DiscoveredPeripheral (CBPeripheral peripheral, NSNumber rssi, NSDictionary advertisementData)
    {
        Console.WriteLine("DiscoveredPeripheral: " +peripheral.Name);

        //If the discovered device's name IS a valid serial number, AND match the serial number that we're intended to establish connection to...
        if (peripheral.Name == "MyPeripheralName") {

            Console.WriteLine("It's the peripheral we're looking for!");

            //Stop scanning...
            StopScanning();

            //Save reference and connect to it!
            CurrentPeripheral = peripheral;
            Central.ConnectPeripheral(CurrentPeripheral, new PeripheralConnectionOptions());
        }
    }

    private void ConnectedPeripheral (CBPeripheral peripheral)
    {
        Console.WriteLine("ConnectedPeripheral: " + peripheral.Name);

        //Great -- explore services and charateristics and don't stop until we know that this device is legit!
        CurrentPeripheral.DiscoveredService +=  (object sender, NSErrorEventArgs e) => DiscoveredServices (e.Error);        
        CurrentPeripheral.DiscoveredCharacteristic += (object sender, CBServiceEventArgs e) => DiscoveredCharacteristics (e.Error, e.Service);

        //Kick service discovery!
        CurrentPeripheral.DiscoverServices();
    }

    private void FailedToConnectPeripheral (NSError error, CBPeripheral peripheral)
    {
        Console.WriteLine("FailedToConnectPeripheral: " + peripheral.Name);
    }

    private void DisconnectedPeripheral (NSError error, CBPeripheral peripheral)
    {
        Console.WriteLine("DisconnectedPeripheral: " + peripheral.Name + "(Error = " + error + ")");
    }

    private void DiscoveredServices (NSError error)
    {
        Console.WriteLine("DiscoveredService: " + error + "  -- " + NSThread.Current.IsMainThread);
        if (error != null) {

            //No error -- great!
            foreach (CBService service in CurrentPeripheral.Services) {
                Console.WriteLine ("Discovered service: " + service);
            }
        } 
        else 
        {
            //Opps -- failed!

            //Disconnect and indicate connection attempt finished
        }
    }

    private void DiscoveredCharacteristics (NSError error, CBService service)
    {
        Console.WriteLine("DiscoveredCharacteristics for service: " + service);
        if (error != null) {

            //No error -- great!
            foreach (CBCharacteristic characteristic in service.Characteristics) {
                Console.WriteLine ("Discovered charateristic: " + characteristic);
            }
        } 
        else 
        {
            //Opps -- failed!

            //Disconnect and indicate connection attempt finished
        }
    }

    #endregion
}

问题在于AFTER发现完成后,扫描停止,应用程序连接到外围设备,我们到达 ConnectedPeripheral(CBPeripheral peripheral)功能,我得到了以下SIGSEGV崩溃堆栈跟踪;

2015-04-21 23:33:37.205 BLE-test[1487:322866] critical: Stacktrace:

2015-04-21 23:33:37.206 BLE-test[1487:322866] critical:   at <unknown> <0xffffffff>
2015-04-21 23:33:37.206 BLE-test[1487:322866] critical:   at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <0xffffffff>
2015-04-21 23:33:37.207 BLE-test[1487:322866] critical:   at UIKit.UIApplication.Main (string[],intptr,intptr) [0x00005] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:62
2015-04-21 23:33:37.207 BLE-test[1487:322866] critical:   at UIKit.UIApplication.Main (string[],string,string) [0x0001c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:45
2015-04-21 23:33:37.208 BLE-test[1487:322866] critical:   at BLEtest.Application.Main (string[]) [0x00008] in /Users/markus/Xamarin/BLE-test/BLE-test/Main.cs:17
2015-04-21 23:33:37.208 BLE-test[1487:322866] critical:   at (wrapper runtime-invoke) object.runtime_invoke_dynamic (intptr,intptr,intptr,intptr) <0xffffffff>

后面跟着我已经象征的本地堆栈跟踪;

Incident Identifier: DCFCEE70-3D12-4F69-8303-777B2959563D
CrashReporter Key:   25c23a5bf636eb183168b18867096174f0d59b79
Hardware Model:      iPhone6,2
Process:             BLE-test [1241]
Path:                /private/var/mobile/Containers/Bundle/Application/C091012A-B1CB-464B-BDA5-B193C9C26BFC/BLE-test.app/BLE-test
Identifier:          com.your-company.BLEtest
Version:             1.0 (1.0)
Code Type:           ARM-64 (Native)
Parent Process:      launchd [1]

Date/Time:           2015-04-21 22:22:57.387 +0200
Launch Time:         2015-04-21 22:22:56.120 +0200
OS Version:          iOS 8.3 (12F70)
Report Version:      105

Exception Type:  EXC_BAD_ACCESS (SIGABRT)
Exception Subtype: KERN_INVALID_ADDRESS at 0x000000000004b2c0
Triggered by Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib          0x0000000195233270 __pthread_kill + 8
1   libsystem_pthread.dylib         0x00000001952d116c pthread_kill + 108
2   libsystem_c.dylib               0x00000001951aab14 abort + 108
3   BLE-test                        0x00000001001f6878 mono_handle_native_sigsegv (mini-exceptions.c:2360)
4   BLE-test                        0x0000000100200554 mono_sigsegv_signal_handler (mini.c:6879)
5   libsystem_platform.dylib        0x00000001952c8958 _sigtramp + 64
6   libobjc.A.dylib                 0x0000000194aa5ea8 _class_getVariable + 160
7   libobjc.A.dylib                 0x0000000194aa5ea8 _class_getVariable + 160
8   libobjc.A.dylib                 0x0000000194a9a934 object_getInstanceVariable + 72
9   BLE-test                        0x00000001002b10b0 get_raw_gchandle (runtime.m:304)
10  BLE-test                        0x00000001002b1044 get_gchandle (runtime.m:311)
11  BLE-test                        0x00000001002b0c6c xamarin_get_gchandle (runtime.m:317)
12  BLE-test                        0x00000001002b0bc0 xamarin_get_nsobject_with_type_for_ptr_created (runtime.m:201)
13  BLE-test                        0x00000001002b9508 xamarin_trampoline (.monotouch-trampoline-setup-callstack.inc:181)
14  CoreBluetooth                   0x0000000182f37198 -[CBPeripheral handleServicesDiscovered:] + 740
15  CoreBluetooth                   0x0000000182f34b38 -[CBPeripheral handleMsg:args:] + 280
16  CoreBluetooth                   0x0000000182f309d0 -[CBCentralManager xpcConnection:didReceiveMsg:args:] + 160
17  libdispatch.dylib               0x00000001950ed990 _dispatch_call_block_and_release + 20
18  libdispatch.dylib               0x00000001950ed950 _dispatch_client_callout + 12
19  libdispatch.dylib               0x00000001950f80a0 _dispatch_queue_drain + 1444
20  libdispatch.dylib               0x00000001950f0a58 _dispatch_queue_invoke + 128
21  libdispatch.dylib               0x00000001950f1db8 _dispatch_main_queue_callback_4CF + 500
22  CoreFoundation                  0x000000018327f7f4 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 8
23  CoreFoundation                  0x000000018327d89c __CFRunLoopRun + 1488
24  CoreFoundation                  0x00000001831a92d0 CFRunLoopRunSpecific + 392
25  GraphicsServices                0x000000018c9c76f8 GSEventRunModal + 164
26  UIKit                           0x0000000187d6efa8 UIApplicationMain + 1484
27  BLE-test                        0x00000001000a0b94 wrapper_managed_to_native_UIKit_UIApplication_UIApplicationMain_int_string___intptr_intptr + 340
28  BLE-test                        0x000000010007e60c UIKit_UIApplication_Main_string___intptr_intptr + 44
29  BLE-test                        0x000000010007e5c8 UIKit_UIApplication_Main_string___string_string + 184
30  BLE-test                        0x0000000100050110 BLEtest_Application_Main_string__ + 160
31  BLE-test                        0x000000010017c06c wrapper_runtime_invoke_object_runtime_invoke_dynamic_intptr_intptr_intptr_intptr + 156
32  BLE-test                        0x0000000100202614 mono_jit_runtime_invoke (mini.c:6737)
33  BLE-test                        0x000000010024d31c mono_runtime_invoke (object.c:2842)
34  BLE-test                        0x00000001002513f8 mono_runtime_exec_main (object.c:4099)
35  BLE-test                        0x00000001002afa34 xamarin_main (monotouch-main.m:400)
36  BLE-test                        0x00000001001ca924 main + 92
37  libdyld.dylib                   0x000000019511aa04 start + 0

观察和想法;

如果在连接后将代理删除到外设,但在我尝试发现其服务之前(这意味着我将永远不会看到发现的结果,只是为了测试),DiscoverService例程会不会导致崩溃;

// CurrentPeripheral.DiscoveredService += (object sender, NSErrorEventArgs e) => ...    
// CurrentPeripheral.DiscoveredCharacteristic += (object sender, ... 

同样,我也试图让代表保持在上面,但是删除对 DiscoverServices 的实际调用,这也可以在不崩溃的情况下工作(这也许是一个愚蠢的测试,但我只是想当我在第一时间分配代表时,排除发生了某些事情。)

我还阅读了其他人对这类问题/错误的经验,有人说这是一个GC问题,即使某个对象仍然被引用,它可能是GC的?

有人还提到在发布模式下工作但不在调试模式下工作 - 所以我已经测试过在发布中运行我的应用程序似乎也可以工作 - 但我没有任何UI,所以我可以'保证实际上有效 - 但我确实知道与SIGSEGV崩溃......

我很困惑。请帮助我理解为什么 Xamarin不喜欢我。只要我了解约束以及我能做什么/不能做什么,我很乐意通过Xamarin规则来调整和玩。

UPPDATE;

我只是试图查看错误是否是回调/事件处理程序 CBPeripheral,或实际的DiscoverServices方法调用。所以我试着打电话 功能并等待500毫秒,然后查看是否已发现服务 我的外围对象;

Console.WriteLine ("List of services (before discovery attempt)");
if (CurrentPeripheral.Services != null)
    foreach (CBService service in CurrentPeripheral.Services) {
        Console.WriteLine ("Service: " + service);
}

CurrentPeripheral.DiscoverServices();
await Task.Delay(500);

Console.WriteLine ("List of services (after discovery attempt)");
if (CurrentPeripheral.Services != null)
    foreach (CBService service in CurrentPeripheral.Services) {
        Console.WriteLine ("Service: " + service);
}

......它有效!因此,这意味着对DiscoverServices的调用有效 很好,但回调没有!

1 个答案:

答案 0 :(得分:1)

好的,问题解决了。当然是一件新手......

当我启动Xamarin Studio并开始这个项目时,我玩了一下,看了看不同的选项,设置,功能,菜单等,只是为了掌握这个概念。我找到的一件事是iOS构建选项,可以更改链接器选项。我认为“链接所有组件”是一个不错的选择,只是为了安全起见,而不是预先选择的“不要链接”。

没有考虑更多,并继续编码。

但是当我发现问题在这个帖子中提出并解释时,你拼命地开始在任何地方搜索任何东西,然后我开始寻找GC问题,这就是错误的根源。所以,通过这个链接(http://developer.xamarin.com/guides/cross-platform/deployment,_testing,_and_metrics/memory_perf_best_practices/)阅读内存管理的最佳实践,我发现了;

The Link All Assemblies should be used with caution as it may break the application in unexpected ways.

因此,我改回不要链接,现在一切都像魅力...... 发现新手错误?是的,当然,但我希望其他人犯同样的错误会发现这篇文章并发现它有用。

谢谢, /马库斯