iOS 項目中如何使用 Python
我所在的公司對項目編譯后的大小和資源文件有嚴格的要求,每次集成發(fā)版對于包體積的增量都是有嚴格的控制监嗜,因此吊输,如何減少包體積是每一個研發(fā)都需要考慮的声畏。
對于包體積大小我們可以從資源文件和編碼來控制啄栓,如何減少項目編譯文件的大小娄帖,只能從代碼層面去進行一些優(yōu)化,如規(guī)范代碼昙楚,合理的使用組合近速、繼承等設計模式。對于資源文件堪旧,如圖片我們可以進行壓縮后在集成削葱。下面就是如何利用python對項目中的使用的圖片進行檢測并實現(xiàn)在線壓縮、上傳等功能淳梦。
正確引入 Python
Python 庫本身就支持 C 直接調用析砸,只要我們能正確的引入即可。git上查找了下谭跨,果然已經有人給我們造好了輪子點這個鏈接干厚,作者已經把對應版本 python 壓縮成 zip 并上傳,可直接下載解壓使用螃宙,當然我們也可以使用提供的 makefile 腳本自行編譯蛮瞄。同時作者還提供了一個快速創(chuàng)建iOS項目的模版點這個鏈接以下是記錄使用過程中的一些問題。
模版項目的安裝
下載作者提供的模版項目到本地點這個鏈接谆扎,查看作者提供的README.rst 文件挂捅,兩個步驟快速生成模版項目(我是使用自己手動創(chuàng)建的項目)
step1 執(zhí)行 $ pip install cookiecutter 安裝 cookiecutter 插件
step2 執(zhí)行 $ cookiecutter https://github.com/pybee/Python-iOS-template --checkout 3.7 安裝指定版本的python
生成的項目的整體目錄結構如下所示
3.8 版本 'cpython/initconfig.h' file not found 錯誤
目前最新的Python版本已經是3.8了,本著使用最新版本的原則堂湖,下載了3.8版本闲先,解壓后引入工程,
編譯項目出現(xiàn)如下錯誤:
通過錯誤提示和頭部標識无蜂,我們發(fā)現(xiàn)這個文件不能直接被引用使用伺糠,在看下所有的 cpython 目錄下文件,除了initconfig.h 文件其他都有 this header file must not be included directly 標識斥季,既然不能直接 included 直接干掉整個 cpython 目錄除 initconfig.h 的所有文件训桶。再次編譯發(fā)現(xiàn)錯誤如下
在查看python headers 目錄下的所有文件,發(fā)現(xiàn)還有外層還有一個 pystate.h 文件酣倾,里面有一個對 cpython 目錄除 pystate.h 引用舵揭,但是剛剛已經被我們刪了
刪除也無法解決問題,說明該方式不對躁锡。查看了下issues里面的問題列表午绳,也沒有發(fā)現(xiàn)該問題和解決方法,既然高版本不行映之,只能使用3.7了拦焚。
3.7 版本
替換項目中的python相關文件蜡坊,直接編譯項目這次更離譜22個錯誤
查看錯誤會發(fā)現(xiàn)這些報錯都是Python目錄的 Resource/lib 文件下的文件,直接把該文件目錄刪除再次運行項目耕漱,這次運行成功了算色。完整的項目截圖如下所示
ps記得引入 libz.tdb 和 libsqlite3.tdb 兩個模塊。
代碼嘗試 No module named 'encodings' 錯誤
直接將模版項目中main文件的代碼粘貼復制到自己項目中螟够,運行項目這次出現(xiàn)了如下錯誤
錯誤是越來越多了,分析下錯誤提示發(fā)現(xiàn)是sqlite3庫問題峡钓,檢查下項目中如下的配置位置妓笙,發(fā)現(xiàn)是libz.tdb 和 libsqlite3.tdb 兩個模塊沒有被導入,按下圖正確導入
再次運行項目能岩,項目運行起來寞宫,但是新的錯誤又出現(xiàn)了
python在初始化的時候失敗了沒有找到 encodings 模塊,想到前面刪除的 Resource/lib 文件目錄拉鹃,那么還是我們對模塊引入的方式有問題辈赋,從網上找了下 iOS 工程中調用Python方法,看到這篇文章有關于Home路徑的設置膏燕,下載了作者提供的代碼钥屈,發(fā)現(xiàn)可以把該模塊制作成 bundle模塊引入項目,并修改原代碼中關于 python_home 項目終于正確的運行起來了坝辫,修改刪除無用的代碼篷就,并創(chuàng)建測試 main.py 文件,控制臺成功打印出日志
完整的 main.m 代碼
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Python.h"
#include <dlfcn.h>
int main(int argc, char *argv[]) {
int ret = 0;
unsigned int I;
NSString *tmp_path;
NSString *python_home;
NSString *python_path;
wchar_t *wpython_home;
const char* nslog_script;
const char* main_script;
wchar_t** python_argv;
@autoreleasepool {
NSString * resourcePath = [[NSBundle mainBundle] resourcePath];
// Special environment to prefer .pyo; also, don't write bytecode
// because the process will not have write permissions on the device.
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);
wpython_home = Py_DecodeLocale([python_home UTF8String], NULL);
Py_SetPythonHome(wpython_home);
// Set the PYTHONPATH
python_path = [NSString stringWithFormat:@"PYTHONPATH=%@/Library/Application Support/com.example.jddd/app:%@/Library/Application Support/com.example.jddd/app_packages", resourcePath, resourcePath, nil];
NSLog(@"PYTHONPATH is: %@", python_path);
putenv((char *)[python_path UTF8String]);
// iOS provides a specific directory for temp files.
tmp_path = [NSString stringWithFormat:@"TMP=%@/tmp", resourcePath, nil];
putenv((char *)[tmp_path UTF8String]);
NSLog(@"Initializing Python runtime...");
Py_Initialize();
main_script = [
[[NSBundle mainBundle] pathForResource:@"main"
ofType:@"py"] cStringUsingEncoding:NSUTF8StringEncoding];
if (main_script == NULL) {
NSLog(@"Unable to locate jddd main module file.");
exit(-1);
}
// If other modules are using threads, we need to initialize them.
PyEval_InitThreads();
@try {
// Start the main.py script
NSLog(@"Running '%s'...", main_script);
FILE* fd = fopen(main_script, "r");
if (fd == NULL) {
ret = 1;
NSLog(@"Unable to open '%s'; abort.", main_script);
} else {
ret = PyRun_SimpleFileEx(fd, main_script, 1);
fclose(fd);
if (ret != 0) {
NSLog(@"Application quit abnormally!");
} else {
// In a normal iOS application, the following line is what
// actually runs the application. It requires that the
// Objective-C runtime environment has a class named
// "PythonAppDelegate". This project doesn't define
// one, because Objective-C bridging isn't something
// Python does out of the box. You'll need to use
// a library like Rubicon-ObjC [1], Pyobjus [2] or
// PyObjC [3] if you want to run an *actual* iOS app.
// [1] http://pybee.org/rubicon
// [2] http://pyobjus.readthedocs.org/
// [3] https://pythonhosted.org/pyobjc/
UIApplicationMain(argc, argv, nil, @"PythonAppDelegate");
}
}
}
@catch (NSException *exception) {
NSLog(@"Python runtime error: %@", [exception reason]);
}
@finally {
Py_Finalize();
}
NSLog(@"Leaving...");
}
exit(ret);
return ret;
}
以上內容參考以下文章