This blog post is about producing better bindings of Objective-C libraries forXamarin.iOS and Xamarin.Mac . Read the series introduction to get a better idea why this is important and how it can save you time and headaches.
What can go wrong ?
Binding a selector using a correct [Export("")]
attribute is only half the job. The .NET method signature must match the one defined in Objective-C. Xamarin.iOS does some magic, like converting string
and NSString
, but you still need to be careful. The most common mistakes are:
- signed/unsigned types : this is often minor and you might want to disable the check on some (or even all) of them. CLS compliance means .NET developers prefer signed over unsigned integers (except for
byte
). Apple APIs often use unsigned integers when a number cannot be negative. For example:
@property (nonatomic, readwrite) NSUInteger loops;
[Export ("loops")] int Loops { get; set; } // should be uint, easier (less casting) if an int
- wrong types : a common case is a missing
ref
when the Objective-C API requires a pointer to a value, not the value itself. For example:
-(void) sphericalRadius:(float*) r zenith:(float*) zenith azimuth:(float*) azimuth;
[Export ("sphericalRadius:zenith:azimuth:")] void GetSphere (float radius, float zenith, float azimuth); // wrong, references to float (ref float) should have been used
- wrong size : this often happens on structures, but it can also occurs when a type name is misleading (e.g.
long
is 32 bits in Objective-C, at least for 32bits archs like i386 and ARM, while it is 64 bits on .NET) or when common usage differs, as in this case:
-(id) initWithDuration:(ccTime)duration red:(GLshort)deltaRed green:(GLshort)deltaGreen blue:(GLshort)deltaBlue;
[Export ("initWithDuration:red:green:blue:")] IntPtr Constructor (float duration, byte red, byte green, byte blue); // wrong, we're almost wired for bytes when seeing RGB but it does not have to be!
What can we check for ?
Quite a lot. It’s possible to get an encoded signature for selectors. The earlierloops
selector, for example, would return the “ I8@0:4
” string value.
From it we can extract the return value ( I
an unsigned int
) and all the parameters, e.g. self ( @
) and the selector ( :
) are always present and there are no other parameters since it’s a getter. We also get the size of the parameters, which can be compared to the size of the managed types used—and they should alwaysmatch!
Using all of the above, we can test pretty closely if an Objective-C signature matches the defined .NET signature. Simple? Not quite.
Objective-C encoding for structures is quite detailed. However, their internal names won’t match the one being used in your bindings. You’ll need to help the fixture to map them by overriding its IsValidStruct
method.
Why is this important ?
Signature mistakes are dangerous. Some APIs might (appear to) work, others will crash immediately (bad) or the crash will occur later (worse) because they will mess up the stack and cause memory corruption. Debugging such issues can be very time consuming, so your time is well invested in using this fixture.
How to fix issues ?
Fixing the .NET signature to match it’s Objectice-C peer is the most common case. The failure will tell you which parameter (or the return value) looks wrong. You have to confirm/fix this using the library’s documentation and/or it’s header files.
One notable (and uncommon) exception is the use of a variable number of arguments (e.g. var_list
in Objective-C, params
in C#). They are not part of the encoded signature, so once confirmed you can skip them by overriding theSkip(Type,MethodInfo,string)
method.
As discussed earlier, you might also want to override Check(char,Type)
to skip the signed/unsigned differences for integer types.
What’s missing ?
The encoded signature is not perfect. All NSObject
parameters are encoded as ‘@
‘ so we cannot detect if an NSData
should have been used instead of NSDate
. Since all objects are pointers, they all will be the same size ( IntPtr.Size
) so this will also not be noticed by the size check. Typos/errors, even if less common, are still possible.
This blog post concludes the five-part series on building better bindings for Xamarin.iOS and Xamarin.Mac. Thanks for reading!
原文链接:
http://blog.xamarin.com/producing-better-bindings-4-signatures/?utm_source=tuicool