我所在的公司對項目編譯后的大小和資源文件有嚴格的要求,每次集成發版對於包體積的增量都是有嚴格的控制,因此,如何減少包體積是每一個研發都需要考慮的。
對於包體積大小我們可以從資源文件和編碼來控制,如何減少項目編譯文件的大小,只能從代碼層面去進行一些優化,如規范代碼,合理的使用組合、繼承等設計模式。對於資源文件,如圖片我們可以進行壓縮后在集成。下面就是如何利用python對項目中的使用的圖片進行檢測並實現在線壓縮、上傳等功能。
正確引入 Python
Python 庫本身就支持 C 直接調用,只要我們能正確的引入即可。git上查找了下,果然已經有人給我們造好了輪子點這個鏈接,作者已經把對應版本 python 壓縮成 zip 並上傳,可直接下載解壓使用,當然我們也可以使用提供的 makefile 腳本自行編譯。同時作者還提供了一個快速創建iOS項目的模版點這個鏈接以下是記錄使用過程中的一些問題。
模版項目的安裝
下載作者提供的模版項目到本地點這個鏈接,查看作者提供的README.rst 文件,兩個步驟快速生成模版項目(我是使用自己手動創建的項目)
step1 執行 $ pip install cookiecutter 安裝 cookiecutter 插件
step2 執行 $ 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版本,解壓后引入工程,
編譯項目出現如下錯誤:

通過錯誤提示和頭部標識,我們發現這個文件不能直接被引用使用,在看下所有的 cpython 目錄下文件,除了initconfig.h 文件其他都有 this header file must not be included directly 標識,既然不能直接 included 直接干掉整個 cpython 目錄除 initconfig.h 的所有文件。再次編譯發現錯誤如下

在查看python headers 目錄下的所有文件,發現還有外層還有一個 pystate.h 文件,里面有一個對 cpython 目錄除 pystate.h 引用,但是剛剛已經被我們刪了

刪除也無法解決問題,說明該方式不對。查看了下issues里面的問題列表,也沒有發現該問題和解決方法,既然高版本不行,只能使用3.7了。
3.7 版本
替換項目中的python相關文件,直接編譯項目這次更離譜22個錯誤

查看錯誤會發現這些報錯都是Python目錄的 Resource/lib 文件下的文件,直接把該文件目錄刪除再次運行項目,這次運行成功了。完整的項目截圖如下所示

ps記得引入 libz.tdb 和 libsqlite3.tdb 兩個模塊。
代碼嘗試 No module named 'encodings' 錯誤
直接將模版項目中main文件的代碼粘貼復制到自己項目中,運行項目這次出現了如下錯誤

錯誤是越來越多了,分析下錯誤提示發現是sqlite3庫問題,檢查下項目中如下的配置位置,發現是libz.tdb 和 libsqlite3.tdb 兩個模塊沒有被導入,按下圖正確導入

再次運行項目,項目運行起來,但是新的錯誤又出現了

python在初始化的時候失敗了沒有找到 encodings 模塊,想到前面刪除的 Resource/lib 文件目錄,那么還是我們對模塊引入的方式有問題,從網上找了下 iOS 工程中調用Python方法,看到這篇文章有關於Home路徑的設置,下載了作者提供的代碼,發現可以把該模塊制作成 bundle模塊引入項目,並修改原代碼中關於 python_home 項目終於正確的運行起來了,修改刪除無用的代碼,並創建測試 main.py 文件,控制台成功打印出日志

完整的 main.m 代碼
#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!"