美文网首页
iOS 项目中使用 Python下

iOS 项目中使用 Python下

作者: 不知道是哪个号 | 来源:发表于2020-09-14 01:58 被阅读0次

    上一节最后我们通过将 Python 所依赖的 Resources 通过 Bundle 成功引入项目。并通过 fopen 方式结合 PyRun_SimpleFileEx 执行成功,对于执行单一方法的 Py 文件来说还是很方便的,但是我们的业务往往比较复杂,需要被拆分成多个文件类,除系统自带的库之外我们也需要引入一些其他的第三方库,同时我们也需要实例化某个具体类,并调用这些类的中的具体方法并且传入参数,那么我们该如何实现呢?

    Py代码/第三方库的引入

    直接引入

    第一种方式比较简单粗暴,我们将自己开发的文件统一放在一个文件目录下,并命名为 ocpy 。并将该文件目录直接放入 Resources 目录中的 python 环境目录下,如下图所示

    7.png

    同样的,我们把运行所依赖的其他第三方资源直接也放在该目录下,如 requests 库

    8.png

    这种方式比较简单,但是同时将 Python环境自有的库、第三方库、和自己的库混合在一起不方便今后的管理。有没有更好的方式来实现当然有,我们可以通过设置 PYTHONPATH 来实现。

    设置 PYTHONPATH

    我们新建一个 PythonCon.bundle 资源文件,把我们自己开发的文件放入里面如下图所示

    9.png

    在设置完 PythonHome 路径后,即可设置 PYTHONPATH 的路径为PythonCon.bundle

    NSString *python_path = [NSString stringWithFormat:@"PYTHONPATH=%@/PythonCon.bundle/", resourcePath, nil];
    putenv((char *)[python_path UTF8String]);
    

    同样的我们可以用该方法设置我们所依赖的第三方库(此处未把第三方库从PythonEnv.bundle中独立出来)。

    Python 类中方法的调用

    模块的导入&类的创建

    以上准备工作完成后,我们便具备对文件类中的方法进行调用了,首先我们需要引入我们需要调用的模块,然后获取该模块中所有的类和方法,获取并初始化我们需要的类

    // 模块的引入
    PyObject *pModule = PyImport_ImportModule([@"ocpy.main" UTF8String]);
    // 获取模块中的类/方法
    PyObject *pDict = PyModule_GetDict(pModule);
    // 获取某个具体的e类/方法
    PyObject *pClass = PyDict_GetItemString(pDict, "JDGFileObject");
    // 创建&初始化该类
    PyObject *pyClass = PyInstanceMethod_New(pClass);
    _pyInstance = PyObject_CallObject(pyClass, NULL);
    

    方法调用

    我们可以使用 PyObject_CallMethod 方法来调用我们所创建类中的方法,PyObject_CallMethod 需要至少传入三个参数,
    第一个即是我们刚刚初始化的类 _pyInstance,第二个参数为我们需要调用的该类中的方法,第三个及以后是调用该方法中需要传入的参数类型(如果没有可直接为 NULL)

    例如我们需要调用 main 文件中的 test 方法,该方法需要两个字符串类型 arg1和arg2参数,并最终返回一个 "success" 结果,最终我们通过使用PyArg_Parse 从 result 对象中成功解析出我们所需要的内容 "success"。

    PyObject *result = PyObject_CallMethod(_pyInstance, [func UTF8String], "(s,s)", [arg1 UTF8String], [arg2 UTF8String]);
    char *resultCString = NULL;
    PyArg_Parse(result, "s", &resultCString);
    NSString *content = [NSString stringWithFormat:@"%s", resultCString];
    NSLog(@"========%@", content);
    

    以上便是整个py类设置调用的过程,完整的代码见文末

    Pod 发布过程中的问题

    当所有工作完成后,我们可以将该工具发布出去供他人使用,而公司内部也有成熟的组件管理工具,非常方便,我们只需要把相关模块发布到系统中,其他人就可以直接在他的模块中使用。我准备把这个检测工具包含 Python 和依赖的 OpenSSL、BZip2、XZ等通过该工具发布到系统中。

    项目编译 Must define SIZEOF_WECHSR_T 问题

    以前发布的组件都是比较常规类型的,没发布过这种依赖二进制文件类型的组件,因此也就没研究过这个工具的发布过程,没想到这一次发布就出现问题了

    1.png

    该工具在发布时,有一个检验的过程,和工具维护人员沟通后了解到公司内部所发布的组件需要同时支持 armv7、arm64 、i386、 x86_64 这四种类型,而作者提供的默认 .a 只包含 arm64 和 x86_64 这两种类型,而我在做测试时一直使用的是 iphone6 及其以上的模拟器,因此一直没有出现这个错误,当我换成 iphone5模拟器去执行时,出现了下面这个错误提示

    4.png

    和上面的错误日志是一样的,还好作者提供了编译脚本项目,通过查看作者提供的脚本 中makefile 文件,打包的时候只支持 arm64 和 x86_64 这两种类型,因此我们只需要对 makefile 进行简单的修改这样就可以解决这个问题。因为我只使用了iOS模块因此我只修改了该模块

    2.png

    修改完成后在该文件的同目录下执行 make iOS 命令完成重新编译。support 目录下的文件就是我们需要的最新文件如下图所示

    3.png

    发布 OpenSSL 问题

    以上问题解决后,再次执行发布功能,这次出现了另外一个错误

    6.png

    错误提示比较少,看不出具体问题,在不引入其他模块进行项目编译、发布等都是是正常的,和工具维护人员也进行了相关沟通,他也进行了尝试,也出现了相同的错误。

    既然常规方式不行,只能通过各种其他方式进行问题排查,拆分各个模块,每次只发布其中一个模块,最后发现只有在发布 openssl 这个模块时出现了这个错误,其他模块单独和一起发布都是正常的,问题缩小就容易解决了,经过了解,openssl 这个模块已经有相关成功组件,那就是自己的原因了,询问了对方是如何进行发布操作的,但是他也不知道,最后把对方发布成功的包给要过来了,经过对比发现,我的文件比对方的要多几个 __DECC_INCLUDE_ 开头的文件,如下图

    5.png

    看了下这些文件,里面无相关代码,只有一些注释,其中有一句 This file is only used by HP C on VMS, and is included automatically,既然在iOS里面用不到,那就直接删了(注意删除其他目录下类似文件),删除后再次发布,这次终于发布成功了。

    完整的代码

    NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
    NSLog(@"resourcePath === %@",resourcePath);
    NSString *python_home;
    putenv("PYTHONOPTIMIZE=1");
    putenv("PYTHONDONTWRITEBYTECODE=1");
    putenv("PYTHONUNBUFFERED=1");
    // Set the home for the Python interpreter
    python_home = [NSString stringWithFormat:@"%@/PythonEnv.bundle/Resources/", resourcePath, nil];
    NSLog(@"PythonHome is: %@", python_home);
    Py_SetPythonHome(Py_DecodeLocale([python_home UTF8String], NULL));
        
    NSString *python_path = [NSString stringWithFormat:@"PYTHONPATH=%@/PythonCon.bundle/", resourcePath, nil];
    putenv((char *)[python_path UTF8String]);
    NSLog(@"PYTHONPATH is: %@", python_path);
    NSLog(@"Initializing Python runtime...");
    Py_Initialize();
    PyEval_InitThreads();
        
    // 模块的引入
    PyObject *pModule = PyImport_ImportModule([@"ocpy.main" UTF8String]);
    // 获取模块中的类/方法
    PyObject *pDict = PyModule_GetDict(pModule);
    // 获取某个具体的e类/方法
    PyObject *pClass = PyDict_GetItemString(pDict, "JDGFileObject");
    // 创建&初始化
    PyObject *pyClass = PyInstanceMethod_New(pClass);
    PyObject *pyInstance = PyObject_CallObject(pyClass, NULL);
    
    // 方法的调用
    PyObject *result = PyObject_CallMethod(pyInstance, [func UTF8String], "(s,s)", [arg1 UTF8String], [arg2 UTF8String]);
    char *resultCString = NULL;
    PyArg_Parse(result, "s", &resultCString);
    NSString *content = [NSString stringWithFormat:@"%s", resultCString];
    NSLog(@"========%@", content);
    

    以上内容参考以下文章

    c-api

    C调用python类的正确方法

    在iOS中运行Python

    相关文章

      网友评论

          本文标题:iOS 项目中使用 Python下

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