iOS SDK应该注意的问题和tips

作者: brownfeng | 来源:发表于2017-03-21 15:12 被阅读182次

    到webank以后一直在做sdk相关的开发,包括微动力,云客服,以及云刷脸项目.其中遇到一些常见的sdk中开发应该避免的误区,以及一些可能会踩到的坑.

    重中之重的前缀

    iOS中使用OC开发,由于OC没有命名空间概念,很大问题是重名问题.有以下几个地方都需要注意添加前缀,前缀一定要有辨识度,类名、宏定义、枚举、通知、类别等命名时加静态库统一特殊前缀,以避免命名冲突:

    • 自定义的类: WBFaceSDKxxxxxxClass
    • category的方法名最好添加: wbfacesdk_xxxxMethod()
    • 对于项目中的c、c++中的方法,需要加前缀,全局c方法: WBFaceSDKCGRectXXXX()
    • 全局常量,包括字符串等等:常见的有通知名称WBFaceSDKxxxxxNotification
    • 使用typedef/NS_ENUM/NS_OPTIONS定义的struc或enum的名称

    第三方公用库或静态库不要打入SDK进行编译

    我们常用的网络库使用AFNetworking,SDWebImage,虽然引入到项目中,但是.m不要勾选"target membership"选项,只需要引入.h就OK.或者通过cocoapod,创建sdk工程,通过cocopads管理项目依赖.

    常见的第三方框架,比如libssl.a,opencv.a,libcrypto.a等等,拖入工程的时候"Add to Target"(taget membership)都不要打勾.也就是sdk对这些公共库有依赖关系,但是最终生成的sdk不包含公共库的源码文件.

    尽量减少使用Category

    iOS中Category使用的很频繁,如果静态库里面使用了category,由于OC的runtime和静态度的静态资源的加载问题.需要进行以下操作:

    那么需要在Build settings->Other Linker Flags中添加-ObjC参数,解决运行时unrecognized selector send to instance的crash错误.

    此外,如果在category的.m文件中只有一个category分类,此时需要额外添加参数-all_load或者使用-force_load /path/to/sdk来强制加载sdk.为了不使用这个参数,最好的方法是在.m文件中,写一个空的Class来占位:

    // Use dummy class for category in static library.
    #ifndef DUMMY_CLASS
    #define DUMMY_CLASS(name) \
        @interface DUMMY_CLASS_ ## name : NSObject @end \
        @implementation DUMMY_CLASS_ ## name @end
    #endif
     
    //使用示例:
    //UIColor+YYAdd.m
    #import "UIColor+YYAdd.h"
    DUMMY_CLASS(UIColor+YYAdd)
     
    @implementation UIColor(YYAdd)
    ...
    @end
    

    使用-all_load或者-force_load xxx在编译期间非常耗时.
    为了减少耗时,本人是将通用的类创建成常用的全局函数,static函数,inline函数(注意添加前缀)

    支持通用平台arm,x86等

    我们可以通过如下命令查询具体.a或者framework文件支持的平台:lipo -info /path/to/.a or lipo -info /*.framework/xxx.

    平时项目开发中,可能使用第三方提供的静态库.a,如果.a提供方技术不成熟,使用的时候就会出现问题,例如:

    • 在真机上编译报错:No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=x86_64, VALID_ARCHS=i386).
    • 在模拟器上编译报错:No architectures to compile for (ONLY_ACTIVE_ARCH=YES, active arch=armv7s, VALID_ARCHS=armv7 armv6).
      要解决以上问题,就要了解一下Apple移动设备处理器指令集相关的一些细节知识。

    通常我们生成.a文件或者static framework默认配置的Valid Architectures支持arm64,armv6,ramv7s.sdk也就只能在真机上跑,如果需要在模拟器中跑,需要使其支持x86_64``i386.

    Xcode中指令集相关选项(Build Setting中):

    • Architectures: 工程被编译成可支持哪些指令集类型,而支持的指令集越多,就会编译出包含多个指令集代码的数据包,对应生成二进制包就越大,也就是ipa包会变大.默认为 Standard architectures(armv7,arm64)
    • Valid Architectures:限制可能被支持的指令集的范围,也就是Xcode编译出来的二进制包类型最终从这些类型产生,而编译出哪种指令集的包,将由Architectures与Valid Architectures(因此这个不能为空)的交集来确定.

    例如:Valid Architectures设置的支持arm指令集版本有:armv7/armv7s/arm64,对应的Architectures设置的支持arm指令集版本有:armv7s,这时Xcode只会生成一个armv7s指令集的二进制包。

    • Build Active Architecture Only:指定是否只对当前连接设备所支持的指令集编译.当这个属性设置是yes时候,一般为了debug速度更快,只编译当前的architecture版本.默认debug:yes, release:no.

    在我们制作sdk时候,要做到比较大的兼容性(iPhone 4,iOS7以上),可以进行如下设置:

    • ValidArchitectures设置为:armv7|armv7s|arm64|i386|x86_64
    • Architectures设置不变(或根据你需要): armv7|arm64

    然后分别选择iOS设备和模拟器进行编译,最后找到相关的.a进行合包.(xctool或者pod帮助打包).如果是手动创建framework,那么使用如下脚本

    if [ "${ACTION}" = "build" ]
    then
    INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
    
    DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
    
    SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
    
    
    if [ -d "${INSTALL_DIR}" ]
    then
    rm -rf "${INSTALL_DIR}"
    fi
    
    mkdir -p "${INSTALL_DIR}"
    
    cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
    #ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
    
    lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
    
    open "${DEVICE_DIR}"
    open "${SRCROOT}/Products"
    fi
    

    同时,如果第三方库不支持模拟式尽量使用预编译指令,屏蔽第三方库:

    #if TARGET_IPHONE_SIMULATOR//模拟器
    
    #elif TARGET_OS_IPHONE//真机
    
    #endif
    

    arm64:iPhone5S以上| iPad Air| iPad mini2(iPad mini with Retina Display)

    armv7s:iPhone5|iPhone5C|iPad4(iPad with Retina Display)

    armv7:iPhone3GS|iPhone4|iPhone4S|iPad|iPad2|iPad3(The New iPad)|iPad mini|iPod Touch 3G|iPod Touch4

    i386 :5及以下模拟器
    x86_64:5s及以上模拟器

    Debug模式,必要Log输出与crash日志收集

    在sdk中要提供完整的Log信息的输出,尤其是错误日志,并且如何处理这个错误的步骤.当要收集crash日志时,可以通过系统NSException中提供的NSSetUncaughtExceptionHandler和NSGetUncaughtExceptionHandler方法,收集一些简单的crash日志信息,然后将收集到的crash日志同步到服务端.

    具体如何进行Log模块的设计,crash日志简单上报暂时没有很好的方案.

    涉及UI界面问题

    需要考虑到界面旋转带来的潜在问题.布局时候尽量使用autolayout.对于必须使用硬编码的地方才使用硬编码.

    对于硬编码,获取屏幕的宽度和高度,与普通的位置不一样:

    #define ScreenHeight MAX([[UIScreen mainScreen] bounds].size.height,[[UIScreen mainScreen] bounds].size.width)//获取屏幕高度,兼容性测试
    #define ScreenWidth  MIN([[UIScreen mainScreen] bounds].size.height,[[UIScreen mainScreen] bounds].size.width)//获取屏幕宽度,兼容性测试
    

    所有的viewController的BaseVC中最好增加以下方法仅支持竖屏:

    #pragma mark - viewController orientation
    - (UIInterfaceOrientationMask)supportedInterfaceOrientations//支持哪些方向
    {
        return UIInterfaceOrientationMaskPortrait;
    }
    
    /**
     初始化自己的方向, 这个在旋转屏幕时候非常重要
     If you do not implement this method, the system presents the view controller using the current orientation of the status bar.
     
     说明如果我们没有override这个方法,系统会根据当前statusbar来决定当前使用的orientation
     */
    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation//默认显示的方向
    {
        return UIInterfaceOrientationPortrait;
    }
    
    /**
     如果返回NO,则无论你的项目如何设置,你的ViewController都只会使用preferredInterfaceOrientationForPresentation的返回值来初始化自己的方向,如果你没有重新定义这个函数,那么它就返回父视图控制器的preferredInterfaceOrientationForPresentation的值。
     */
    - (BOOL)shouldAutorotate//是否支持旋转屏幕
    {
        return NO;
    }
    

    所有资源提供到bundle中,命名问题

    ...

    sdk测试完整性

    ...

    SDK中多次使用缓存状态问题

    在调用SDK时,一定要先清理SDK原有的配置状态,缓存状态等等!!!!

    参考文献

    写iOS SDK注意事项

    iOS 如何创建和使用静态库

    IOS生成同时支持armv7,armv7s,i386,x86_64,arm64的静态库.a文件

    iOS开发~制作同时支持armv7,armv7s,arm64,i386,x86_64的静态库.a

    apple官方文档-制作framework

    iOS开发——创建你自己的Framework

    用lipo合并模拟器Framework与真机Framework

    相关文章

      网友评论

        本文标题:iOS SDK应该注意的问题和tips

        本文链接:https://www.haomeiwen.com/subject/djnmgttx.html