Xamarin.iOS使用Objective-C靜態類庫.a(Linking Native Libraries)

 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,一般放命令行尾部,需要時添加

     完整的命令,以百度地圖爲例:

[csharp] view plaincopy
  1. --registrar:legacy -cxx -gcc_flags "-L${ProjectDir} -framework MapKit -framework CoreLocation -framework QuartzCore  -framework OpenGLES  -framework SystemConfiguration -framework CoreGraphics -framework Security -ObjC"  
     命令添加在UI工程中,工程選項>>iOS Build>>Additional Options>>Additional mtouch arguments     

     5.C#與ObjC數據類型

     

    瞭解完Xamarin.iOS的基礎所需,正式開始接下來繁多無味而重要的映射方法binding工作,當然xamarin官方也提供了一個自動生成工具,但這個工具給我感覺不太能用,也許沒深入研究吧,感興趣的朋友可以下載試用一下【objective_sharpie】(如果你還無法開始時,建議參考【友盟SDK的調用】)。 

    開始之前簡單介紹一下:
一個頭文件中可以出現多個@interface,每個@interface在C#中都是一個類,例如:BMKActionPaopaoView.h文件:

[objc] view plaincopy
  1. @interface BMKActionPaopaoView : UIView  
  2. - (id)initWithCustomView:(UIView*)customView;  
  3. @end  

[csharp] view plaincopy
  1. //BMKActionPaopaoView.h  
  2. [BaseType (typeof(UIView))]  
  3. interface BMKActionPaopaoView  
  4. {  
  5.     /// <summary>  
  6.     /// [構造函數模式]  
  7.     /// 初始化並返回一個BMKActionPaopaoView  
  8.     /// - (id)initWithCustomView:(UIView*)customView;  
  9.     /// </summary>  
  10.     /// <param name="customView">Custom view.</param>  
  11.     [Export ("initWithCustomView:")]  
  12.     IntPtr Constructor (UIView customView);  
  13. }  


    1.打開StructsAndEnums.cs文件,如其名字一樣這裏是編寫結構體和枚舉的,也是最簡單的一塊:
     1)枚舉(Enums)
     打開頭文件[BMKAnnotationView.h],可以看到原生代碼:
[objc] view plaincopy
  1.    enum {  
  2.     BMKAnnotationViewDragStateNone = 0,      ///< 靜止狀態.  
  3.     BMKAnnotationViewDragStateStarting,      ///< 開始拖動  
  4.     BMKAnnotationViewDragStateDragging,      ///< 拖動中  
  5.     BMKAnnotationViewDragStateCanceling,     ///< 取消拖動  
  6.     BMKAnnotationViewDragStateEnding         ///< 拖動結束  
  7. };  
  8. typedef NSUInteger BMKAnnotationViewDragState;  
[csharp] view plaincopy
  1. public enum BMKAnnotationViewDragState  
  2. {  
  3.     None = 0,      ///< 靜止狀態.  
  4.     Starting,      ///< 開始拖動  
  5.     Dragging,      ///< 拖動中  
  6.     Canceling,     ///< 取消拖動  
  7.     Ending         ///< 拖動結束  
  8. }  
    簡單的把有含義的單詞提取出來,以方便在C#中使用,枚舉無非就是使用整數0、1、2代表各實際含意。
    2)結構體(Structs)
    打開頭文件[BMKType.h],可以看到原生代碼:
[objc] view plaincopy
  1. typedef struct {  
  2.     CLLocationCoordinate2D center;  ///< 中心點經緯度座標  
  3.     BMKCoordinateSpan span;     ///< 經緯度範圍  
  4. } BMKCoordinateRegion;  
[csharp] view plaincopy
  1. public struct BMKCoordinateSpan  
  2. {  
  3.     public double latitudeDelta;  
  4.     public double longitudeDelta;  
  5. }  
   2.Binding APIs
      打開ApiDefinition.cs,以下將開始對頭文件中出現的原生語法類型進行介紹

      1)Binding Methods(基礎方法)

[objc] view plaincopy
  1. - (void)setCoordinate:(CLLocationCoordinate2D)newCoordinate;  
  2. + (BMKArcline *)arclineWithPoints:(BMKMapPoint *)points;  
  3. + (BMKCircle *)circleWithCenterCoordinate:(CLLocationCoordinate2D)coord radius:(CLLocationDistance)radius;  
     C#代碼,“+”代表靜態方法,加Statice標識;當遇到不是基本數據類型時,例如[BMKCircle],可找着對應頭文件,先進行其對象binding
[csharp] view plaincopy
  1. [Export ("setCoordinate:")]  
  2. void SetCoordinate (CLLocationCoordinate2D newCoordinate);  
  3.   
  4. [Static,Export ("arclineWithPoints:")]  
  5. BMKArcline ArclineWithPoints (BMKMapPoint points);  
  6.   
  7. [Static,Export ("circleWithCenterCoordinate:radius:")]  
  8. BMKCircle CircleWithCenterCoordinate (CLLocationCoordinate2D coord, double radius);  
       帶out/ref parameters的方法
[objc] view plaincopy
  1. - (void) someting:(int) foo withError:(NSError **) retError;  
  2. - (void) someString:(NSObject **)byref;  
[csharp] view plaincopy
  1. [Export ("something:withError:")]  
  2. void Something (int foo, out NSError error);  
  3. [Export ("someString:")]  
  4. void SomeString (ref NSObject byref);  
2)Binding Properties(屬性)

[objc] view plaincopy
  1. @property (nonatomic) CGPoint calloutOffset;  
  2. @property (nonatomicgetter=isSelected) BOOL selected;  
  3. @property (nonatomicgetter=isDraggable) BOOL draggable __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_2);  
  4. @property (nonatomicretain) NSArray   *POIs;  
      C#代碼,相關對應[Memory management attributes內存管理標識]ArgumentSemantic標識有三個:Assign,Copy,Retain,依情況而定;與靜態方法同樣如果是帶“+”,也要進行[Static]標識

[csharp] view plaincopy
  1. [Export ("calloutOffset")]  
  2. PointF CalloutOffset{ getset; }  
  3.   
  4. [Export ("selected")]  
  5. bool Selected{ [Bind ("isSelected")]getset; }  
  6.   
  7. [Export ("draggable")]  
  8. bool Draggable{ [Bind ("isDraggable")]getset; }  
  9.   
  10. [Export ("POIs", ArgumentSemantic.Retain)]  
  11. NSArray POIs{ getset; }  
     3)Binding Constructors(構造函數)
[objc] view plaincopy
  1. - (id)initWithCustomView:(UIView*)customView;  
C#對應,id可以理解爲一個實例化對象
[csharp] view plaincopy
  1. [Export ("initWithCustomView:")]  
  2. IntPtr Constructor (UIView customView);  
     4)Binding Protocols(委託協議)
Protocol需要在interface對應頭上添加[Model][Protocol],以標明爲Delegate
[objc] view plaincopy
  1. @protocol BMKLocationServiceDelegate <NSObject>  
  2. @optional  
  3. - (void)willStartLocatingUser;  
  4. - (void)didStopLocatingUser;  
  5. @end  
C#對應,@optional可選標識不用標識
[csharp] view plaincopy
  1. [Model][Protocol]  
  2. [BaseType (typeof(NSObject))]  
  3. interface BMKLocationServiceDelegate  
  4. {  
  5.     [Export ("willStartLocatingUser")]  
  6.     void willStartLocatingUser ();  
  7.   
  8.     [Export ("didStopLocatingUser")]  
  9.     void didStopLocatingUser ();  
  10. }  
而需要使用委託協議方法的對應類中,需要這樣實現

[objc] view plaincopy
  1. @property (nonatomic, assign) id<BMKLocationServiceDelegate> delegate;  
C#對應,Assign與屬性標識一致,根據實際添加
[csharp] view plaincopy
  1. [Export ("delegate", ArgumentSemantic.Assign)]  
  2. NSObject WeakDelegate { getset; }  
  3.   
  4. [Wrap ("WeakDelegate")]  
  5. BMKLocationServiceDelegate Delegate { getset; }  
     5)Binding Class Extensions

還未使用過:

[csharp] view plaincopy
  1. [BaseType (typeof (UIResponder))]  
  2. interface UIView {  
  3.         [Bind ("drawAtPoint:withFont:")]  
  4.         SizeF DrawString ([Target] string str, PointF point, UIFont font);  
  5. }  
     6)Binding Objective-C Argument Lists
[objc] view plaincopy
  1. - (void) appendWorkers:(XWorker *) firstWorker, ... NS_REQUIRES_NIL_TERMINATION ;  
[csharp] view plaincopy
  1. [Export ("appendWorkers"), Internal]  
  2. void AppendWorkers (Worker firstWorker, IntPtr workersPtr)  
     7)Binding Fields
Sometimes you will want to access public fields that were declared in a library.
還未使用過:
[csharp] view plaincopy
  1. [Field ("NSSomeEventNotification")]  
  2. NSString NSSomeEventNotification { get; }  
     8)Binding Notifications(通知)
還未使用過:
[csharp] view plaincopy
  1. interface MyClass {  
  2.     [Notification]  
  3.     [Field ("MyClassDidStartNotification")]  
  4.     NSString DidStartNotification { get; }  
  5. }  
     9)Binding Categories(類別)

這個百度地圖上出現比較多,還有幾個問題官網沒提到的問題:

[objc] view plaincopy
  1. @interface BMKMapView (LocationViewAPI)  
  2. /// 問題出現在此  
  3. @property (nonatomicBOOL showsUserLocation;  
  4.   
  5. -(void)updateLocationData:(BMKUserLocation*)userLocation;  
  6. @end  
[Category]標識聲明;文檔中只提到了對方法的解析,但沒有對屬性進行說明,如果就按之前對屬性進行解析,編譯時會報錯cannot declare instance members in a static class(不能在靜態類中聲明實例成員),語法上的錯誤,根據要對其進行改造,利用ObjC上get,set構造器的規則進行賦取值修改,這時showsUserLocation就可以用了

[csharp] view plaincopy
  1. [Category,BaseType (typeof(BMKMapView))]  
  2. public partial interface LocationViewAPI  
  3. {  
  4.     [Export ("setShowsUserLocation:")]  
  5.     void SetShowsUserLocation(bool isShow);  
  6.   
  7.     [Export ("updateLocationData:")]  
  8.     void UpdateLocationData (BMKUserLocation userLocation);  
  9. }  
     10)Binding Blocks(可理解爲Action)

ObjC是爲了避免Protocol的混亂而設計的

[objc] view plaincopy
  1. (void)asyncInitWithSuccess:(void (^)())onSuccess failure:(void (^)(NSError *))onFailure;
[csharp] view plaincopy
  
  1. [Export ("asyncInitWithSuccess:failure:")]
  2. void AsyncInitWithSuccess (Action onSuccess, Action<NSError> onFailure); 
     11)Asynchronous Methods(異步方法)
這個api中也比較少見
[csharp] view plaincopy
  1. [Export ("loadfile:completed:")]  
  2. [Async]  
  3. void LoadFile (string file, Action<string> completed);  

     12)Binging [typedef] 

     使用typedef爲現有類型創建同義字,定義易於記憶的類型名

[objc] view plaincopy
  1. typedef void(^ESTCompletionBlock)(NSError* error);  
  2. typedef void(^ESTUnsignedCompletionBlock)(unsigned value, NSError* error);  
  3. typedef void(^ESTBoolCompletionBlock)(BOOL value, NSError* error);  
  4. typedef void(^ESTStringCompletionBlock)(NSString* value, NSError* error);  
[csharp] view plaincopy
  1. public delegate void ESTCompletionBlock(NSError error);  
  2. public delegate void ESTUnsignedCompletionBlock(byte value, NSError error);  
  3. public delegate void ESTBoolCompletionBlock(bool value, NSError error);  
  4. public delegate void ESTStringCompletionBlock(NSString value, NSError error);  
   3.備註

     1)NSArray數組

由於ObjC中直接以NSObjce進行操作,因此有些時候並不知道NSArray的對象到底是何種數據類型,但如果在解析過程中,你很明確知道時,可以這樣解析

[objc] view plaincopy
  1. - (NSArray *)getPeerViews ();  
  2. - (void) setViews:(NSArray *) views  
[csharp] view plaincopy
  1. [Export ("getPeerViews")]  
  2. UIView [] GetPeerViews ();  
  3.   
  4. [Export ("setViews:")]  
  5. void SetViews (UIView [] views);  
     2)NSString類型,可以直接使用string替換
     3)委託協議的使用

[objc] view plaincopy
  1. @interface Demo : NSObject <UIAlertViewDelegate>  
  2. @end  
[csharp] view plaincopy
  1. [BaseType (typeof (NSObject), KeepUntilRef="Dismiss"),   
  2. Delegates=new string [] { "WeakDelegate" }, Events=new Type [] { typeof (SomeDelegate) }) ]  
  3. class Demo {  
  4.     [Export ("show")]  
  5.     void Show (string message);  
  6. }  
     4)public partial的使用
在Binding Categories(類別)中,必須使用public partial [interface LocationViewAPI]標識,否則就無法成功調用。



                       
發佈了53 篇原創文章 · 獲贊 9 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章