Xamarin靜態類庫的binding實際上是一個C#與Objective-C方法間映射過程,由於第三方SDK對iOS開發至關重要,因此官方文檔中也對這塊也做了完整的介紹(Binding Objective-C Libraries),但對於一個完全入門級的程序員來說,這塊還是有諸多麻煩,並且部分Api類型文檔上也未曾提到。下面將以百度地圖作爲案例,全面解析靜態類庫binding工程的知識與問題(Mac OS 10.8.4及以上系統)。生成dll過程:
在開始之前需要了解幾個iOS原生開發中的基礎知識:
1.Command Line Tools for Xcode(XCode命令行工具)
裏面包含了一些常用的命令,在處理靜態類庫時經常用到,有兩種安裝方法:
1)是在Xcode設置>>Downloads更新安裝;
2)使用Mac os下“終端”中敲入命令 xcode-select --install 安裝即可。
2.靜態類庫(.a)編譯類型
由於iOS編譯機制上的區分,靜態類庫分爲模擬器(i386),真機(ARMv7/ARMv7s/ARMv64等),這是由設備CPU決定的。一般廠商提供的靜態類庫都會分別提供(例如BaiduMap:Release-iphoneos(真機)/Release-iphonesimulator(模擬器)),那在沒有明確標識的情況下,我們怎麼區分這些靜態類庫是真機還是模擬器呢?簡單直觀的查看方法,打開Xamarin Studio,新建BindingProject,然後將[libBaidu.a]添加到工程上,打開自動生成的linkwith.cs文件就可以查看到靜態類庫的類型【可參考Monotouch
BindingProject之友盟SDK】。
3.真機模擬器通用靜態類庫.a生成
爲什麼要使用這種通用類庫?方便你模擬器真機調試。由於通用包比原來增加接近一倍,因此發佈的時候記得只使用真機包,以減少.ipa的大小。操作方法:
1)在桌面新建文件夾[BaiduLib],打開終端,輸入命令cd,並且拖動[BaiduLib]到終端命令後面,按回車(意在將文件默認保存在此文件夾下)。
2)接着輸入命令lipo -create [libbaidumapapi.a] [libbaidumapapi.a] -output libBaiduSDK.a,(其中[libbaidumapapi.a] [libbaidumapapi.a]只要拖動到終端框內即可)敲回車,稍等片刻就可以在文件夾[BaiduLib]下看到[libBaiduSDK.a],將其添加到Binding工程中就可以查看到三種類型都存在:LinkTarget.Simulator
| LinkTarget.ArmV7 | LinkTarget.ArmV7s
3) 輸入命令lipo -info libBaiduSDK.a 來檢測.a文件支持的系統架構。
4.Framework系統框架引用
幾乎每個靜態類庫的實現都引用了或多或少的系統framework,規範的靜態類庫引用都會提醒你使用了哪些系統framework,而在xcode中添加是件簡單的事,但在xamarin studio中地卻要使用gcc命令來添加。文檔中有介紹:【Linking
Your Library】
注意幾個關鍵字:
1)-gcc_flags,普通native library編譯
2)-cxx,包含C++代碼類型編譯
3)-L${ProjectDir},當前文件目錄
4)-framework,框架類型,例如:[-framework CFNetwork]
5)--registrar:legacy,引用文檔解釋:The new registrars
(first introduced in 6.2.6) are now enabled by default. They catch many more errors and will prevent many bugs or undefined runtime behaviors. It is possible to fallback to the previous registrars by using --registrar:legacy as an additional mtouch argument。簡單來說應該是編譯器版本問題的一個控制開關;
6)-ObjC,一般放命令行尾部,需要時添加
完整的命令,以百度地圖爲例:
- --registrar:legacy -cxx -gcc_flags "-L${ProjectDir} -framework MapKit -framework CoreLocation -framework QuartzCore -framework OpenGLES -framework SystemConfiguration -framework CoreGraphics -framework Security -ObjC"
5.C#與ObjC數據類型
瞭解完Xamarin.iOS的基礎所需,正式開始接下來繁多無味而重要的映射方法binding工作,當然xamarin官方也提供了一個自動生成工具,但這個工具給我感覺不太能用,也許沒深入研究吧,感興趣的朋友可以下載試用一下【objective_sharpie】(如果你還無法開始時,建議參考【友盟SDK的調用】)。
開始之前簡單介紹一下:
一個頭文件中可以出現多個@interface,每個@interface在C#中都是一個類,例如:BMKActionPaopaoView.h文件:
- @interface BMKActionPaopaoView : UIView
- - (id)initWithCustomView:(UIView*)customView;
- @end
- //BMKActionPaopaoView.h
- [BaseType (typeof(UIView))]
- interface BMKActionPaopaoView
- {
- /// <summary>
- /// [構造函數模式]
- /// 初始化並返回一個BMKActionPaopaoView
- /// - (id)initWithCustomView:(UIView*)customView;
- /// </summary>
- /// <param name="customView">Custom view.</param>
- [Export ("initWithCustomView:")]
- IntPtr Constructor (UIView customView);
- }
1.打開StructsAndEnums.cs文件,如其名字一樣這裏是編寫結構體和枚舉的,也是最簡單的一塊:
1)枚舉(Enums)
打開頭文件[BMKAnnotationView.h],可以看到原生代碼:
- enum {
- BMKAnnotationViewDragStateNone = 0, ///< 靜止狀態.
- BMKAnnotationViewDragStateStarting, ///< 開始拖動
- BMKAnnotationViewDragStateDragging, ///< 拖動中
- BMKAnnotationViewDragStateCanceling, ///< 取消拖動
- BMKAnnotationViewDragStateEnding ///< 拖動結束
- };
- typedef NSUInteger BMKAnnotationViewDragState;
- public enum BMKAnnotationViewDragState
- {
- None = 0, ///< 靜止狀態.
- Starting, ///< 開始拖動
- Dragging, ///< 拖動中
- Canceling, ///< 取消拖動
- Ending ///< 拖動結束
- }
2)結構體(Structs)
打開頭文件[BMKType.h],可以看到原生代碼:
- typedef struct {
- CLLocationCoordinate2D center; ///< 中心點經緯度座標
- BMKCoordinateSpan span; ///< 經緯度範圍
- } BMKCoordinateRegion;
- public struct BMKCoordinateSpan
- {
- public double latitudeDelta;
- public double longitudeDelta;
- }
打開ApiDefinition.cs,以下將開始對頭文件中出現的原生語法類型進行介紹
1)Binding Methods(基礎方法)
- - (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate;
- + (BMKArcline *)arclineWithPoints:(BMKMapPoint *)points;
- + (BMKCircle *)circleWithCenterCoordinate:(CLLocationCoordinate2D)coord radius:(CLLocationDistance)radius;
- [Export ("setCoordinate:")]
- void SetCoordinate (CLLocationCoordinate2D newCoordinate);
- [Static,Export ("arclineWithPoints:")]
- BMKArcline ArclineWithPoints (BMKMapPoint points);
- [Static,Export ("circleWithCenterCoordinate:radius:")]
- BMKCircle CircleWithCenterCoordinate (CLLocationCoordinate2D coord, double radius);
- - (void) someting:(int) foo withError:(NSError **) retError;
- - (void) someString:(NSObject **)byref;
- [Export ("something:withError:")]
- void Something (int foo, out NSError error);
- [Export ("someString:")]
- void SomeString (ref NSObject byref);
- @property (nonatomic) CGPoint calloutOffset;
- @property (nonatomic, getter=isSelected) BOOL selected;
- @property (nonatomic, getter=isDraggable) BOOL draggable __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);
- @property (nonatomic, retain) NSArray *POIs;
- [Export ("calloutOffset")]
- PointF CalloutOffset{ get; set; }
- [Export ("selected")]
- bool Selected{ [Bind ("isSelected")]get; set; }
- [Export ("draggable")]
- bool Draggable{ [Bind ("isDraggable")]get; set; }
- [Export ("POIs", ArgumentSemantic.Retain)]
- NSArray POIs{ get; set; }
- - (id)initWithCustomView:(UIView*)customView;
- [Export ("initWithCustomView:")]
- IntPtr Constructor (UIView customView);
Protocol需要在interface對應頭上添加[Model][Protocol],以標明爲Delegate
- @protocol BMKLocationServiceDelegate <NSObject>
- @optional
- - (void)willStartLocatingUser;
- - (void)didStopLocatingUser;
- @end
- [Model][Protocol]
- [BaseType (typeof(NSObject))]
- interface BMKLocationServiceDelegate
- {
- [Export ("willStartLocatingUser")]
- void willStartLocatingUser ();
- [Export ("didStopLocatingUser")]
- void didStopLocatingUser ();
- }
- @property (nonatomic, assign) id<BMKLocationServiceDelegate> delegate;
- [Export ("delegate", ArgumentSemantic.Assign)]
- NSObject WeakDelegate { get; set; }
- [Wrap ("WeakDelegate")]
- BMKLocationServiceDelegate Delegate { get; set; }
還未使用過:
- [BaseType (typeof (UIResponder))]
- interface UIView {
- [Bind ("drawAtPoint:withFont:")]
- SizeF DrawString ([Target] string str, PointF point, UIFont font);
- }
- - (void) appendWorkers:(XWorker *) firstWorker, ... NS_REQUIRES_NIL_TERMINATION ;
- [Export ("appendWorkers"), Internal]
- void AppendWorkers (Worker firstWorker, IntPtr workersPtr)
Sometimes you will want to access public fields that were declared in a library.
還未使用過:
- [Field ("NSSomeEventNotification")]
- NSString NSSomeEventNotification { get; }
還未使用過:
- interface MyClass {
- [Notification]
- [Field ("MyClassDidStartNotification")]
- NSString DidStartNotification { get; }
- }
這個百度地圖上出現比較多,還有幾個問題官網沒提到的問題:
- @interface BMKMapView (LocationViewAPI)
- /// 問題出現在此
- @property (nonatomic) BOOL showsUserLocation;
- -(void)updateLocationData:(BMKUserLocation*)userLocation;
- @end
- [Category,BaseType (typeof(BMKMapView))]
- public partial interface LocationViewAPI
- {
- [Export ("setShowsUserLocation:")]
- void SetShowsUserLocation(bool isShow);
- [Export ("updateLocationData:")]
- void UpdateLocationData (BMKUserLocation userLocation);
- }
ObjC是爲了避免Protocol的混亂而設計的
- - (void)asyncInitWithSuccess:(void (^)())onSuccess failure:(void (^)(NSError *))onFailure;
- [Export ("asyncInitWithSuccess:failure:")]
- void AsyncInitWithSuccess (Action onSuccess, Action<NSError> onFailure);
這個api中也比較少見
- [Export ("loadfile:completed:")]
- [Async]
- void LoadFile (string file, Action<string> completed);
12)Binging [typedef]
使用typedef爲現有類型創建同義字,定義易於記憶的類型名
- typedef void(^ESTCompletionBlock)(NSError* error);
- typedef void(^ESTUnsignedCompletionBlock)(unsigned value, NSError* error);
- typedef void(^ESTBoolCompletionBlock)(BOOL value, NSError* error);
- typedef void(^ESTStringCompletionBlock)(NSString* value, NSError* error);
- public delegate void ESTCompletionBlock(NSError error);
- public delegate void ESTUnsignedCompletionBlock(byte value, NSError error);
- public delegate void ESTBoolCompletionBlock(bool value, NSError error);
- public delegate void ESTStringCompletionBlock(NSString value, NSError error);
1)NSArray數組
由於ObjC中直接以NSObjce進行操作,因此有些時候並不知道NSArray的對象到底是何種數據類型,但如果在解析過程中,你很明確知道時,可以這樣解析
- - (NSArray *)getPeerViews ();
- - (void) setViews:(NSArray *) views
- [Export ("getPeerViews")]
- UIView [] GetPeerViews ();
- [Export ("setViews:")]
- void SetViews (UIView [] views);
3)委託協議的使用
- @interface Demo : NSObject <UIAlertViewDelegate>
- @end
- [BaseType (typeof (NSObject), KeepUntilRef="Dismiss"),
- Delegates=new string [] { "WeakDelegate" }, Events=new Type [] { typeof (SomeDelegate) }) ]
- class Demo {
- [Export ("show")]
- void Show (string message);
- }
在Binding Categories(類別)中,必須使用public partial [interface LocationViewAPI]標識,否則就無法成功調用。