Marshalling a NSObject already within MonoTouch to

2019-09-01 06:35发布

问题:

I have a 3rd party Obj-C library (that I have written MonoTouch bindings for) that returns data via the NSDictionary parameter within the UIImagePickerControllerDelegate's FinishedPickingMedia function (ZBar iPhone SDK for those interested)

I have bound the class of the instance that I expect to be stored in this dictionary (ZBarSymbol).

As I expected, the MonoTouch runtime couldn't possibly check through all these collection instances when marshalling, and my MonoTouch code only sees an instance of NSObject in the dictionary (If this assumption is wrong and it should be a ZBarSymbol instance, can someone please let me know).

I attempted to manually marshal the NSObject over into a ZBarSymbol using the following code:

public override void FinishedPickingMedia (UIImagePickerController picker, NSDictionary info)
{
    var result = info[ZBarSDK.BarcodeResultsKey];
    var symbol = result as ZBarSymbol;
    if ( symbol != null )
    {
        // This never works obviously.   
    }
    else
    {
        symbol = new ZBarSymbol(result.Handle);
        Console.WriteLine("Data = " + symbol.Data);
    }
}

The constructor of the ZBarSymbol taking the pointer works fine. However attempting to access any members (e.g. the Data property results in the following exception:

MonoTouch.Foundation.MonoTouchException: Objective-C exception thrown.  
Name: NSInvalidArgumentException  
Reason: -[ZBarSymbolSet data]: unrecognized selector sent to instance 0x8d2960  
    at ZBar.ZBarSymbol.get_Data () [0x00000] in <filename unknown>:0  
    at ZBarMonoTouchTest.ZBarMonoTouchTestViewController+BarcodeReaderCallback.FinishedPickingMedia (MonoTouch.UIKit.UIImagePickerController picker, MonoTouch.Foundation.NSDictionary info) [0x00197] in /Users/tyson/Projects/ZBarMonoTouchTest/ZBarMonoTouchTestViewController.cs:112  
    at MonoTouch.UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00042] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:29  
    at ZBarMonoTouchTest.Application.Main (System.String[] args) [0x00000] in /Users/tyson/Projects/ZBarMonoTouchTest/Main.cs:17  

So is it possible to marshal these classes over within the application code? If so, how? Or even better, if someone could show me how to setup the bindings so this happens automatically (but I'm not sure this is possible).

回答1:

As I expected, the MonoTouch runtime couldn't possibly check through all these collection instances when marshalling, and my MonoTouch code only sees an instance of NSObject in the dictionary (If this assumption is wrong and it should be a ZBarSymbol instance, can someone please let me know).

Turns out I was wrong - the MonoTouch runtime will perform this automatic marshalling for you, even with instances within NSDictionarys.

The problem with the above was that it wasn't returning a ZBarSymbol at all - it was returning a ZBarSymbolSet. If you look closely the exception actually hints at that - "Reason: -[ZBarSymbolSet data]:"



回答2:

In case anyone is interested how this mysterious zBar delegate works... after some fighting.. zBarSymbolSet has an IEnumerator! this returns a properZBarSymbol that you can use the .Data property to get a result barcode. This also passes it up as a subscribable Event:

public class zScannerDelegate : ZBarReaderDelegate
{
public delegate void ScanResult(string scanstrring);
public event ScanResult ScannedCode;
public zScannerDelegate ()
{
}
public override void FinishedPickingMedia (UIImagePickerController picker, NSDictionary info)
{

    ZBarSymbolSet result = null;

    string retstr = string.Empty;
    foreach (var sresult in info.Values) {
        if (sresult is ZBarSymbolSet) {
            result = sresult as ZBarSymbolSet;
            break;
        }
    }
    if (result != null) {
        foreach (var itema in result) {
            Console.WriteLine (itema.Data);
            retstr = itema.Data;
            ScanResult ret = ScannedCode;
            if (ret != null)
                ret(retstr);
        }
    }
}



}