Xamarin的坑-绑定(三) - iOS 绑定宝典
2017-10-07

编者语:iOS 绑定,对于Xamarin来说,是难点,也是痛点。这也阻碍了Xamarin的推广和真正进入一些大型项目,像上段时间两个例子微信和高德SDK绑定,随着版本更新也需要进行调整。这几个月我收到了不少留言和邮件是希望我总结一些步骤和实践的经验,由于工作繁忙,我拖到了现在实在不好意思,今天就来总结一把。
       绑定原生库(Linking Native Libraries)
       iOS的第三方库有两种,一种是Static Library ,一种是Dynamic Framework。 你可以把这些库对应成.NET中的DLL。封装成这些原生库一般是通过Objective-C/Swift/C去进行封装。通过Sharpie的转换,实际上是C#和这些Objective-C/Swift/C方法进行对接。这里再谈谈Sharpie对应生成两个文件ApiDefinition.cs/Structs.cs .(如图)
       
       不少人说这两个文件就埋下了不少坑,转换后第一步在ApiDefinition.cs就一堆         
   [Verify(ConstantsInterfaceAssociation)] 
       这个提示更多是需要你确认,而不需要有太多恐惧。我经验告诉大家,先把这些注释掉就可以了,有这种提示你是无法编译通过的。需要修改基本上是一些属性设定,get/set . 个人觉得这是第一道坎,也是最轻松的。
       修改完,真正的大坑才会出现。写代码是一个学问,不可能每个人都有高质量的代码。封装库就是了。因为ApiDefinition是做C#到Objecitve-C转换的桥梁。你需要理解好Protocol / Category / Interface 搞懂。
       Protocol等价于C#的接口,Category等价于C#的扩展方法,Interface等价于一个类的声明它结合Implement组合成一个类
       在ApiDefinition.cs中,这些都有对应的标识符作划分,这里通常呈现的问题如下:
       1. 转换的时候,由于Objective-C的语法问题很容易把几个方法重名
       2. 还有全局静态方法,会有多个
          [Static]
     partial interface Constants   
           这个时候你需要把它整合放到一个里面
       3. 指针,C#是没有指针概念的,但Objective-C有 ,转换的时候有时候不会消除*,这个时候你只需要把*去掉
       4. 还有就是继承,
           在Objective-C上
           protocol VCSessionDelegate <XXXClass>
           转换时有时候会转换成
           [Protocol, Model]
           interface VCSessionDelegate :XXXClass
           这时候会有XXXClass出错的信息,但你实际已经定义了XXXClass,个人建议修正如下,减少错误发生
           [Protocol, Model]
     [BaseType(typeof(XXXClass))]
           interface VCSessionDelegate
       5. Sharpie 由于是一个通用的转换,总会有一些不如意的地方, 它做得最好的是一些对类和方法的注释,你可以根据它的备注去调
            整或修正。我多次的项目经验证明多看注释是非常明智的。
       在Structs.cs中是一些通过C语言定义的方法,和一些结构体为主。这里也得注意几个问题
       1. 是结构体的类型,它通常会转换成nint,你得修改为uint/int/long/ulong
       2. 还有C方法里面,有一些方法里面的参数自带了函数体,如
           [DllImport ("__Internal")]
     static extern unsafe uint BASS_StreamCreateFileUser (uint system, uint flags, BASS_FILEPROCS proc, uint user);    
           这个时候你需要做一个委托去定义BASS_FILEPROCS
           public delegate void BASS_FILEPROCS(int Handle, int Channel, int Data, IntPtr User);
       3. C方法有时候需要调用,你需要把C接口调用暴露出去
           [DllImport ("__Internal")]
     static extern unsafe uint BASS_StreamCreateFileUser (uint system, uint flags, BASS_FILEPROCS proc, uint user); 
           那暴露出去就需要
           [DllImport ("__Internal",EntryPoint = "BASS_Init")]         public static extern unsafe uint BASS_StreamCreateFileUser (uint system, uint flags, BASS_FILEPROCS proc, uint user); 
         
       4. 调用C方法,还得注意,如2提到,我们用委托去取代了用函数作为参数,但Xamarin.iOS是不支持通过委托作为参数作为方法参数定义,当你要调用时需要作特别处理,如我们定义了
              public delegate void SYNCPROC(int Handle, int Channel, int Data, IntPtr User);
          [DllImport ("__Internal",EntryPoint = "BASS_ChannelSetSync")]
     [Verify (PlatformInvoke)]
  public static extern int BASS_ChannelSetSync (uint handle, SyncFlags type, long param,   SYNCPROC proc, IntPtr user);

          调用时,你需要做的是通过MonoPInvokeCallback去定义一个静态变量,绕过委托不能用作方法参数的问题

           [MonoPInvokeCallback(typeof(BASS.SYNCPROC))]
     static void SYNCPROCCallback(int Handle, int Channel, int Data, IntPtr User)
     {
            Console.WriteLine("8888");
     }
   5.有些常量定义Sharpie转换是没有的,个人建议多看看库的文档,把值找出来也是必须要的,否则对不上,写好了也是没办法调用。
   估计你把上面的ApiDefinition.cs和Structs.cs调整好,就可以编译通过了。但在实际引用后,会陆续出现报错问题,这里也得花上不少时间,我总结出几个问题 
   1. 如果你是引用.a别忘记把.a放进iOS调用项目中,否则它是编译不通过的
   2. 无论你是.a或者Framework的binding,你都别忘记把一些对底层依赖库进行添加,否则你是不能编译通过的,添加依赖有两种方式,一种在binding项目里,一种是iOS调用项目里的mtouch argument里面。我更建议大家在Binding项目里面添加好,减少实际项目的配置。如
   
   3. 编译的时候,还需要对build option进行调整
   
   这里有两个关键的地方,一个是LinkBehavior,一个是mtouch arguments。
   LinkBehavior有三种属性Don't Link/Link Framework SDKs Only/Link All,依据你项目所需去设置。由于我们已经把库的依赖关系做在Binding上了,所以个人选择是Don't Link.如果没有你就需要添加mtouch了,这个有点复杂,个人是不建议这样做。至于LinkAll还是算了,这个很垃圾,我从来没有编译成功过。

   mtouch arguments在必要时还是得写写,这个东西主要描述一些gcc/framework/force_load问题,如果你binding配置好根本就没有什么必要。
   绑定是技术活,你别指望一次半次就能解决,建议多做多写。我把我的宝典都给大家了大笑有疑问多交流。

原文地址: http://blog.csdn.net/kinfey/article/details/72857055