Xcode7 Framework探索

作者: 秦明Qinmin | 来源:发表于2016-04-14 15:20 被阅读1172次

    在设计SDK或其它框架的时候,我们大多数不想将自己的源码暴露给别人。在iOS中可行的办法有静态库和静态Framework(虽然苹果今年来开放了动态Framework,但是目前项目中用的最多还是前面两种)。最开始在Xcode中制作静态Framework是很麻烦的,制作静态库要简单的多,毕竟Xcode带了这样一选项。但是使用Xcode6或7的时候发现自带了和静态库制作一样的功能,因此制作动态库已经很简单了。

    1. Framework静态库

    其中最重要的设置就是将Mach-O Type设为Static Library,因为默认创建Framework的时候是动态库Dynamic Library类型。

    步骤
    a. 点击创建Framework选项

    1.png

    b. 设置Mach-O Type为Static Library。(如果要支持bitcode, 还需要在TAGETS的Build setting中搜索Other C Flags,添加命令“-fembed-bitcode”)。

    24543C0F-87D3-4673-BDD3-3C50B1C097E1.png C8929E52-A3B6-4481-8978-A4CC69D94EA5.png

    如果没有加cflags可能在使用的时候出现以下错误:


    3957B515-0B53-496A-B099-5F1ECD334E77.png

    c. 设置头文件类型。(Public(公共的),这里存放供其他人查看的header。Private(私有的)这里存放私有的Header,以上两个Headers存放位置都会暴露出来,所有人可以查看。有些Header是不想给别人看到的。这种header放在第三个类Project中。设置的时候直接将工程中的头文件拖到对应的区域)。如果你用了Category,别人在用你的Framework时会发生崩溃。这时别人在引用时需要在工程中other linker flags中添加-objC如果依然有问题,再添加-all_load。

    757C152E-2C8B-47EE-A424-1D9C832A1F4B.png

    d.选择通用iOS设备build

    如果需要制作模拟器和真机通用版本的可以使用shell脚本在命令行构建,也可以在Xcode新建个build的target,添加构建脚本。
    a. 新建target


    FA3219B5-2D38-4BA5-8E32-EA61F3C0735F.png

    b. 为target添加Run Script,这样就可以在项目工程文件的Products目录生成通用的静态FrameWork。


    0C150FC0-6E11-41E0-9F16-4070E5D485AB.png
    # Sets the target folders and the final framework product.
    # 如果工程名称和Framework的Target名称不一样的话,要自定义FMKNAME
    # 例如: FMK_NAME = "MyFramework"
    FMK_NAME=${PROJECT_NAME}
    
    # Install dir will be the final output to the framework.
    # The following line create it in the root folder of the current project.
    INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
    
    # Working dir will be deleted after the framework creation.
    WRK_DIR=build
    DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
    SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
    
    # -configuration ${CONFIGURATION}
    # Clean and Building both architectures.
    xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos -arch armv7 -arch armv7s -arch arm64 clean build
    xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator -arch x86_64 clean build
    
    # Cleaning the oldest.
    if [ -d "${INSTALL_DIR}" ]
    then
        rm -rf "${INSTALL_DIR}"
    fi
    
    mkdir -p "${INSTALL_DIR}"
    
    cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
    
    # Uses the Lipo Tool to merge both binary files
    lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
    
    rm -r "${WRK_DIR}"
    
    open "${SRCROOT}/Products/"
    
    echo ${FMK_NAME}
    

    c. 查看库的架构可以通过file或者lipo命令来查看

    BE5F7DE5-962F-4C78-963D-D1B21A874DD9.png

    在使用的时候(链接状态为Required),我们通过MachOView查看可执行文件,并不能看见动态链接了我们的FrameWork,因为静态库编译进了二进制可执行文件中,并不需要动态链接。


    7C928AAD-0F66-4A00-92FA-97D19F2C6A68.png

    2. Framework动态库

    默认情况下如果我们不强制设置Mach-O Type为Static Library,那么编译出来Framework就是动态库。

    a. 使用Xcode自动链接动态Framework。此时我们只需要在Embedded Binaries中添加需要使用的动态Framework,如下图:


    EF75F2CF-B8BC-4F7C-ABED-12A43559407C.png

    添加之后在Build Phases中会多出一项Embed Fraworks,它的作用也就是拷贝动态库到Runpath Search Paths目录。


    FA54044E-9958-4D97-AB27-4D23F73B11B6.png

    至于app运行的时候如何找到动态库,我们可以设置Runpath Search Paths路径。默认会在@executable_path/Frameworks目录中找,@executable_path/表示可执行文件所在路径,即沙盒中的.app目录,注意不要漏掉最后的/。


    FE464BE5-93B6-436D-B173-A3C1F3D06D5E.png

    编译好,进入到生成的.app文件的根目录,发现Framework已经拷贝到了Frameworks目录(Runpath Search Paths路径)。


    7A1C1BF9-71EA-4FF0-B71A-84C9C540642D.png

    通过MachOView查看可执行文件,这时候发现程序需要动态链接我们刚创建的Framework。


    378BD826-9F2A-4F16-91DE-7A1634A4525C.png

    在使用的过程中如果出现ld: warning: embedded dylibs/frameworks only run on iOS 8 or later警告,这是因为工程默认编译设置的是Dynamic Framework。这种编译只有在iOS8以后才能使用,因此需要设置工程最低支持iOS8.0


    68611C0C-A325-44DB-852E-31C58747E98F.png

    b. dlopen或NSBundle加载动态库
    动态库中真正的可执行代码为KGSDK.framework/KGSDK文件,因此使用dlopen时如果仅仅指定加载动态库的路径为KGSDK.framework是没法成功加载的。使用时将动态库传到特定目录,然后手动加载,最后用runtime来执行相关操作。

    - (IBAction)clickButton:(id)sender
    {
        NSString *documentsPath = [NSString stringWithFormat:@"%@/Documents/KGSDK.framework/KGSDK",NSHomeDirectory()];
        [self openDylibWithPath:documentsPath];
    }
    
    - (void)openDylibWithPath:(NSString *)path
    {
        void* libHandle = NULL;
        libHandle = dlopen([path cStringUsingEncoding:NSUTF8StringEncoding], RTLD_NOW);
        if (libHandle == NULL) {
            char *error = dlerror();
            NSLog(@"dlopen error: %s", error);
        } else {
            NSLog(@"dlopen load framework success.");
        }
    }
    
    - (void)openDylib
    {
        NSString *documentsPath = [NSString stringWithFormat:@"%@/Documents/KGSDK.framework",NSHomeDirectory()];
        NSError *err = nil;
        NSBundle *bundle = [NSBundle bundleWithPath:documentsPath];
        if ([bundle loadAndReturnError:&err]) {
            NSLog(@"bundle load framework success.");
        } else {
            NSLog(@"bundle load framework err:%@",err);
        }
    }
    

    相关文章

      网友评论

      • 3dcc28a75a5f:这个动态库是不会增加ipa包大小的吗

      本文标题:Xcode7 Framework探索

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