iosswiftobjective-cautomatic-ref-countingobjective-c++

Memory management issue while Call Obj-C in Swift


I'm currently developing a native plugin for a Capcitor app. I have very little experience with native iOS development, Swift and Obj-C.

I use a framework (FunSDK) which is written in C++. I call this in an Objective-C++ wrapper. As described here: https://medium.com/@cecilia.humlelu/set-up-c-library-dependencies-in-swift-projects-5dc2ccd2ddaf

The wrapper is then available in Swift via the bridging header. This works so far, I can call the functions from the framework and they are executed correctly. However, the functions of the framework are executed asynchronously. When a result is obtained, the “OnFunSDKResult” method is always executed.

During this step I get an EXC_BAD_ACCESS error while debugging. Then I ticked “Zombie objects” under the diagnostics settings. This causes execution to stop with an EXC_BREAKPOINT.

As I understand it, after executing the iniXMSDK method, the FunSDKWrapper class is cleared from memory before executing the "OnFunSDKResult" method?

I have also already integrated a completionHandler, which is only called in the "OnFunSDKResult" method so that data is transferred to the Swift class. However, that doesn't help either.

I have no idea how to proceed. I hope somebody can help me.

If you need any more information, I would be happy to provide it.

The FunSDKWrapper.mm

//
//  FunSDKWrapper.m
//  App
//
//  Created by Sysprobs on 12/8/23.
//

#import "FunSDKWrapper.h"
#import "FunSDK/FunSDK.h"

#import <XMNetInterface/Reachability.h>

@implementation FunSDKWrapper

-(NSString *)iniXMSDK:(int)test completion:(void (^)(int result))completionHandler{
    
    self.initCompletionHandler = completionHandler;
    
    self.msgHandle = FUN_RegWnd((__bridge void *)self);
    
    FUN_Init();
    Fun_LogInit(self.msgHandle, "", 0, "", LOG_UI_MSG);
    FUN_XMCloundPlatformInit("xxx", "xxx", "xxx", 1);
    FUN_InitNetSDK();
    
    FUN_SetFunStrAttr(EFUN_ATTR_SAVE_LOGIN_USER_INFO,SZSTR([self GetDocumentPathWith:@"UserInfo.db"]));
        
    FUN_SetFunStrAttr(EFUN_ATTR_USER_PWD_DB, SZSTR([self GetDocumentPathWith:@"password.txt"]));
    
    FUN_SetFunStrAttr(EFUN_ATTR_UPDATE_FILE_PATH,SZSTR([self GetDocumentPathWith:@""]));
    FUN_SetFunStrAttr(EFUN_ATTR_TEMP_FILES_PATH,SZSTR([self GetDocumentPathWith:@""]));
        
    FUN_SetFunIntAttr(EFUN_ATTR_AUTO_DL_UPGRADE, 0);
    
    FUN_SetFunStrAttr(EFUN_ATTR_CONFIG_PATH,SZSTR([self GetDocumentPathWith:@"APPConfigs"]));
    
    FUN_SetFunIntAttr(EFUN_ATTR_SUP_RPS_VIDEO_DEFAULT, 1);
    
    FUN_SetFunIntAttr(EFUN_ATTR_SET_NET_TYPE, [self getNetworkType]);
    
    FUN_SysInit("arsp.xmeye.net;arsp1.xmeye.net;arsp2.xmeye.net", 15010);
    FUN_InitNetSDK();

    FUN_SysGetDevList(self.msgHandle, SZSTR(@"xxxx") , SZSTR(@"xxxx"),0);
        
    return @"test";
}

- (void)searchLanDevices {
     FUN_DevSearchDevice(self.msgHandle, 4000, 0);
}


// NSDocument/fileName
- (NSString *)GetDocumentPathWith:(NSString *) fileName {
    NSString* path = [self documentsPath];
    if (fileName != nil) {
        path = [path stringByAppendingString:@"/"];
        path = [path stringByAppendingString:fileName];
    }
    return path;
}
//NSDocument
- (NSString *)documentsPath {
    NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *path = [pathArray lastObject];
    return path;
}

-(int)getNetworkType {
    Reachability*reach=[Reachability reachabilityWithHostName:@"www.apple.com"];
    
    //判断当前的网络状态
    switch([reach currentReachabilityStatus]){
            
        case ReachableViaWiFi:
            return 1;
            
        case ReachableViaWWAN:
            return 2;
            
        default:
            return 0;
            break;
    }
}


- (void)OnFunSDKResult:(NSNumber *) pParam {
    NSInteger nAddr = [pParam integerValue];
    MsgContent *msg = (MsgContent *)nAddr;
    switch (msg->id) {
        case EMSG_SYS_GET_DEV_INFO_BY_USER:{
            self.initCompletionHandler(1);  
            if (msg->param1 < 0){
                //fehler
                NSLog(@"Fehler beim XMCloud login");
            }else{
                
                char devJson[750*500];
                FUN_GetFunStrAttr(EFUN_ATTR_GET_USER_ACCOUNT_DATA_INFO, devJson, 750*500);
                NSLog(@"Geraeteliste: = %s",devJson);
            }
        }
        break;
    }
}


@end

The Swift Class

import Foundation
import Capacitor

/**
 * Please read the Capacitor iOS Plugin Development Guide
 * here: https://capacitorjs.com/docs/plugins/ios
 */
@objc(xmsdkPlugin)
public class xmsdkPlugin: CAPPlugin {
    private let implementation = xmsdk()

    @objc func echo(_ call: CAPPluginCall) {
        let value = call.getString("value") ?? ""
        call.resolve([
            "value": implementation.echo(value)
        ])
    }
    
    @objc func initXMSDK(_ call: CAPPluginCall) {
        //let devId = call.getString("devId") ?? ""
        
           
        let wrapper = FunSDKWrapper();
        let resp = wrapper.iniXMSDK(1, completion: {(result) -> Void in
            NSLog("Completion von iniXMSDK")
        });
        
        call.resolve([
            "status": resp
        ])

    }    
    
}

Errorlog with EXC_BREAKPOINT:

App`invocation function for block in UI_SendMsg(int, XMSG*):
    0x102bed8f4 <+0>:   sub    sp, sp, #0x70
    0x102bed8f8 <+4>:   stp    x22, x21, [sp, #0x40]
    0x102bed8fc <+8>:   stp    x20, x19, [sp, #0x50]
    0x102bed900 <+12>:  stp    x29, x30, [sp, #0x60]
    0x102bed904 <+16>:  add    x29, sp, #0x60
    0x102bed908 <+20>:  mov    x19, x0
    0x102bed90c <+24>:  ldr    x8, [x0, #0x20]
    0x102bed910 <+28>:  ldr    w9, [x8, #0x18]
    0x102bed914 <+32>:  str    w9, [sp, #0x8]
    0x102bed918 <+36>:  ldr    q0, [x8, #0x20]
    0x102bed91c <+40>:  stur   q0, [sp, #0xc]
    0x102bed920 <+44>:  ldr    x9, [x8, #0x38]
    0x102bed924 <+48>:  ldr    x10, [x8, #0x50]
    0x102bed928 <+52>:  stp    x10, x9, [sp, #0x20]
    0x102bed92c <+56>:  ldr    w9, [x8, #0x30]
    0x102bed930 <+60>:  str    w9, [sp, #0x34]
    0x102bed934 <+64>:  str    x8, [sp, #0x38]
    0x102bed938 <+68>:  adrp   x0, 6283
    0x102bed93c <+72>:  add    x0, x0, #0x60             ; g_wndIndexLock
    0x102bed940 <+76>:  bl     0x102bdd34c               ; XBASIC::CLock::Lock at Lock.cpp:58:29
    0x102bed944 <+80>:  adrp   x8, 6283
    0x102bed948 <+84>:  ldr    x10, [x8, #0x38]
    0x102bed94c <+88>:  cbz    x10, 0x102bed98c          ; <+152> at UIInterface.mm:130:24
    0x102bed950 <+92>:  ldr    w9, [x19, #0x28]
    0x102bed954 <+96>:  adrp   x11, 6283
    0x102bed958 <+100>: add    x11, x11, #0x38           ; g_id_wnd + 8
    0x102bed95c <+104>: mov    x8, x11
    0x102bed960 <+108>: ldr    w12, [x10, #0x20]
    0x102bed964 <+112>: cmp    w12, w9
    0x102bed968 <+116>: cset   w12, lt
    0x102bed96c <+120>: csel   x8, x8, x10, lt
    0x102bed970 <+124>: ldr    x10, [x10, w12, uxtw  #3]
    0x102bed974 <+128>: cbnz   x10, 0x102bed960          ; <+108> [inlined] std::__1::less<int>::operator()(int const&, int const&) const at operations.h:487:17
    0x102bed978 <+132>: cmp    x8, x11
    0x102bed97c <+136>: b.eq   0x102bed98c               ; <+152> at UIInterface.mm:130:24
    0x102bed980 <+140>: ldr    w10, [x8, #0x20]
    0x102bed984 <+144>: cmp    w9, w10
    0x102bed988 <+148>: b.ge   0x102bed9dc               ; <+232> at UIInterface.mm:126:21
    0x102bed98c <+152>: adrp   x0, 6283
    0x102bed990 <+156>: add    x0, x0, #0x60             ; g_wndIndexLock
    0x102bed994 <+160>: bl     0x102bdd354               ; XBASIC::CLock::Unlock at Lock.cpp:63:31
    0x102bed998 <+164>: adrp   x0, 4324
    0x102bed99c <+168>: add    x0, x0, #0xb00            ; @"NO MSG Object....."
    0x102bed9a0 <+172>: bl     0x1038af9a0               ; symbol stub for: NSLog
    0x102bed9a4 <+176>: ldr    x0, [x19, #0x20]
    0x102bed9a8 <+180>: ldr    x9, [x0, #0x8]
    0x102bed9ac <+184>: ldaxr  x8, [x9]
    0x102bed9b0 <+188>: sub    x10, x8, #0x1
    0x102bed9b4 <+192>: stlxr  w11, x10, [x9]
    0x102bed9b8 <+196>: cbnz   w11, 0x102bed9ac          ; <+184> [inlined] InterlockedDecrement(long*) at OS.h:120:9
    0x102bed9bc <+200>: sub    w8, w8, #0x1
    0x102bed9c0 <+204>: cmp    w8, #0x0
    0x102bed9c4 <+208>: b.gt   0x102beda24               ; <+304> at UIInterface.mm:143:5
    0x102bed9c8 <+212>: tbnz   w8, #0x1f, 0x102beda18    ; <+292> [inlined] XBASIC::IReferable::Release() at Referable.h:95:17
    0x102bed9cc <+216>: ldr    x8, [x0]
    0x102bed9d0 <+220>: ldr    x8, [x8, #0x8]
    0x102bed9d4 <+224>: blr    x8
    0x102bed9d8 <+228>: b      0x102beda24               ; <+304> at UIInterface.mm:143:5
    0x102bed9dc <+232>: ldp    x20, x21, [x8, #0x28]
    0x102bed9e0 <+236>: adrp   x0, 6283
    0x102bed9e4 <+240>: add    x0, x0, #0x60             ; g_wndIndexLock
    0x102bed9e8 <+244>: bl     0x102bdd354               ; XBASIC::CLock::Unlock at Lock.cpp:63:31
    0x102bed9ec <+248>: cbz    x20, 0x102bed998          ; <+164> at UIInterface.mm:139:13
    0x102bed9f0 <+252>: adrp   x8, 4330
    0x102bed9f4 <+256>: ldr    x0, [x8, #0x5e8]
    0x102bed9f8 <+260>: add    x2, sp, #0x8
    0x102bed9fc <+264>: bl     0x1038b5a00               ; objc_msgSend$numberWithUnsignedInteger:
    0x102beda00 <+268>: mov    x3, x0
    0x102beda04 <+272>: mov    x0, x20
    0x102beda08 <+276>: mov    x2, x21
    0x102beda0c <+280>: mov    w4, #0x1
    0x102beda10 <+284>: bl     0x1038b5aa0               ; objc_msgSend$performSelectorOnMainThread:withObject:waitUntilDone:
->  0x102beda14 <+288>: b      0x102bed9a4               ; <+176> at UIInterface.mm:142:9
    0x102beda18 <+292>: adrp   x0, 3297
    0x102beda1c <+296>: add    x0, x0, #0xd93            ; "Check Please Error(IReferable)!"
    0x102beda20 <+300>: bl     0x1038b2364               ; symbol stub for: puts
    0x102beda24 <+304>: ldp    x29, x30, [sp, #0x60]
    0x102beda28 <+308>: ldp    x20, x19, [sp, #0x50]
    0x102beda2c <+312>: ldp    x22, x21, [sp, #0x40]
    0x102beda30 <+316>: add    sp, sp, #0x70
    0x102beda34 <+320>: ret    

Image with Error Message

Update after Comments:

Thanks in advance for the information.

Unfortunately, the documentation for the FunSDK is not really good. This is the documentation: https://developer.jftech.com/docs/?menusId=8af0e7f3d4af49eab71cfdd8d7e47cef&siderid=6caa41621abd4e689b21a3c0339e8cd6&lang=en

And this is a demo app in which all functions of the framework are used: https://gitlab.xmcloud.io/demo/FunSDKDemo_iOS

The documentation does not describe exactly how to call the "OnFunSDKResult" method. At one point in the DemoApp there is only this comment: "All FUN interfaces with callback information will call back into this method."

The EXC_BAD_ACCESS error also only occurs when I implement the "OnFunSDKResult" method. So I assume that the framework somehow returns the data to the "OnFunSDKResult" method via the "UI_SendMsg".

I now basically understand that ARC released the wrapper. From the tutorials on memory management I still haven't figured out how to change this in my case.

Can someone give me a code example of how I can ensure that ARC only releases the wrapper at a certain time? I also read that I can explicitly disable ARC for individual classes and take over memory management myself. Is this an option so that I only release the memory manually again in the "OnFunSDKResult"?


Solution

  • I was now able to solve the problem by creating a strong reference from the FunSDKWrapper and the CompletionHandler.

    var funSDKWrapper: FunSDKWrapper?
    
    @objc func initXMSDK(_ call: CAPPluginCall) {
        
        let wrapper = FunSDKWrapper();
        self.funSDKWrapper = wrapper;
        
        let strongCompletionHandler: (([AnyHashable: Any]) -> Void) = { result in
            NSLog("Completion von iniXMSDK");
        }
        
        let resp = wrapper.iniXMSDK(1, completion: strongCompletionHandler);
        
        call.resolve([
            "status": resp
        ])
    
    }