更人性化的找出 iOS 中未使用的圖

unImage.png

痛點(diǎn)

刪除 iOS 項(xiàng)目中沒(méi)有用到的圖片市面上已經(jīng)有很多種方式,但是我試過(guò)幾個(gè)都不能很好地滿(mǎn)足我的需求,因此使用 Python 寫(xiě)了這個(gè)腳本,它可能也不能很好的滿(mǎn)足你的需求,因?yàn)檫@種靜態(tài)查找始終會(huì)存在問(wèn)題,每個(gè)人寫(xiě)的代碼風(fēng)格不一,導(dǎo)致匹配字符不一。所以只有掌握了腳本的寫(xiě)法,才能很好的滿(mǎn)足自己的需求。如果你的項(xiàng)目中使用 OC,而且使用純代碼布局,使用這個(gè)腳本完全沒(méi)有問(wèn)題。當(dāng)然你可以修改腳本來(lái)達(dá)到自己的需求。本文主要希望能夠幫助更多的讀者節(jié)省更多時(shí)間做一些有意義的工作,避免那些乏味重復(fù)的工作。

如何使用:

  • 1.修改 DESPATH 為你項(xiàng)目的路徑;
  • 2.直接在腳本所在的目錄下,打開(kāi)終端執(zhí)行 python unUseImage.py,這里的 unUseImage.py 為腳本文件名。你可以在 這里 找到腳本文件;
  • 3.執(zhí)行完成后,桌面會(huì)出現(xiàn)一個(gè) unUseImage 文件夾。文件夾中的 error.log 文件記錄了可能存在未匹配到圖片的文件目錄,image.log 記錄了項(xiàng)目中沒(méi)使用的圖片路徑,images 存放了未使用到的圖片。

重要提示:

當(dāng)確認(rèn) images 文件夾中含有正在使用的圖時(shí),復(fù)制圖片名字到 EXCEPT_IMAGES 中,再次執(zhí)行腳本,確認(rèn) images 文件夾中不再包含使用的圖后,修改 IS_OPEN_AUTO_DELTrue,執(zhí)行腳本,腳本將自動(dòng)清除所有未使用的圖。

腳本:

# coding=utf-8 

import os
import re
import shutil

# 是否開(kāi)啟自動(dòng)刪除,開(kāi)啟后當(dāng)檢查到未用到的圖,
# 將自動(dòng)被刪除。建議確認(rèn)所有的圖沒(méi)用后開(kāi)啟
IS_OPEN_AUTO_DEL = False

# 將要解析的項(xiàng)目名稱(chēng) 
DESPATH = "/Users/wangsuyan/desktop/project/Shopn/Shopn"

# 可能檢查出錯(cuò)的圖片,需要特別留意下
ERROR_DESPATH = "/Users/wangsuyan/Desktop/unUseImage/error.log"

# 解析結(jié)果存放的路徑
WDESPATH = "/Users/wangsuyan/Desktop/unUseImage/image.log"

# 項(xiàng)目中沒(méi)有用到的圖片
IMAGE_WDESPATH = "/Users/wangsuyan/Desktop/unUseImage/images/"

# 目錄黑名單,這個(gè)目錄下所有的圖片將被忽略
BLACK_DIR_LIST = [
    DESPATH + '/ThirdPart', # Utils 下所有的文件將被忽略 
]

# 已知某些圖片確實(shí)存在,比如像下面的圖,腳本不會(huì)自動(dòng)檢查出,需要手動(dòng)加入這個(gè)數(shù)組中
# NSString *name = [NSString stringWithFormat:@"loading_%d",i];
# UIImage *image = [UIImage imageNamed:name];
EXCEPT_IMAGES = [
    'loading_',
    'launch-guide'
]

# 項(xiàng)目中所有的圖
source_images = dict()
# 項(xiàng)目中所有使用到的圖
use_images = set()
# 異常圖片
err_images = set()

# 目錄是否在黑名單中 BLACK_DIR_LIST
def isInBlackList(filePath):
    if os.path.isfile(filePath):
        return filename(filePath) in BLACK_DIR_LIST
    if filePath:
        return filePath in BLACK_DIR_LIST
    return False

# 是否為圖片
def isimage(filePath):
    ext = os.path.splitext(filePath)[1]
    return ext == '.png' or ext == '.jpg' or ext == '.jpeg' or ext == '.gif'

# 是否為 APPIcon
def isappicon(filePath):
    return 'appiconset' in filePath

def filename(filePath):
    return os.path.split(filePath)[1]

def is_except_image(filePath):
    name = filename(filePath)
    for item in EXCEPT_IMAGES:
        if item in name:
            return True
    return False

def auto_remove_images():
    f = open(WDESPATH, 'r')
    for line in f.readlines():
        path = DESPATH + line.strip('\n')
        if not os.path.isdir(path):
            if 'Assets.xcassets' in line:
                path = os.path.split(path)[0]
                if os.path.exists(path):
                    shutil.rmtree(path)
            else:
                os.remove(path)


def un_use_image(filePath):
    if re.search(r'\w@3x.(png|jpg|jpeg|gif)', filePath):
        return

    if re.search(r'\w(@2x){0,1}.(png|jpg|jpeg|gif)', filePath):
        exts = os.path.splitext(filePath)
        result = (filename(filePath).replace('@2x', '')).replace(exts[1],'')
        source_images[result] = filePath

def find_image_name(filePath):
    f = open(filePath)
    for index, line in enumerate(f):
        line = line.strip()
        regx = r'\[\s*UIImage\s+imageNamed\s*:\s*@"(.+?)"'
        matchs = re.findall(regx, line)
        if matchs:
            for item in matchs:
                use_images.add(item)
        else:
            err_matchs = re.findall(r'\[UIImage imageNamed:', line)
            if err_matchs:
                name = filename(filePath)
                for item in err_matchs:
                    err_images.add(str(index + 1) + ':' + name + '\n' + line + '\n')

def find_from_file(path):
    paths = os.listdir(path)
    for aCompent in paths:
        aPath = os.path.join(path, aCompent)
        if isInBlackList(aPath):
            print('在黑名單中,被自動(dòng)忽略' + aPath)
            continue
        if os.path.isdir(aPath):
            find_from_file(aPath)
        elif os.path.isfile(aPath) and isimage(aPath) and not isappicon(aPath) and not is_except_image(aPath):
            un_use_image(aPath)
        elif os.path.isfile(aPath) and os.path.splitext(aPath)[1]=='.m':
            find_image_name(aPath)

if __name__ == '__main__':
    if os.path.exists(IMAGE_WDESPATH):
        shutil.rmtree(IMAGE_WDESPATH)

    os.makedirs(IMAGE_WDESPATH)

    wf = open(WDESPATH, 'w')
    find_from_file(DESPATH)
    for item in set(source_images.keys()) - use_images:
        value = source_images[item]
        wf.write(value.replace(DESPATH, '') + '\n')
        ext = os.path.splitext(value)[1]
        shutil.copyfile(value, IMAGE_WDESPATH + item + ext)

    wf.close()

    ef = open(ERROR_DESPATH, 'w')
    for item in err_images:
        ef.write(item)
    ef.close()

    if IS_OPEN_AUTO_DEL:
        auto_remove_images()

推薦閱讀

【iOS 國(guó)際化】如何把國(guó)際化時(shí)需要3天的工作量縮減到10分鐘

===== 我是有底線的 ======
喜歡我的文章,歡迎關(guān)注我的新浪微博 Lefe_x,我會(huì)不定期的分享一些開(kāi)發(fā)技巧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,893評(píng)論 25 709
  • 22年12月更新:個(gè)人網(wǎng)站關(guān)停,如果仍舊對(duì)舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,390評(píng)論 22 257
  • 作為女生,好像天生就對(duì)食物特別的喜歡,不管走到哪里都會(huì)想要吃東西。同時(shí),作為女生,二十多歲一過(guò),人的新陳代謝就開(kāi)始...
    isabellaLilove閱讀 290評(píng)論 0 0
  • 文 | 晨?jī)?每天差不多都是相同的重復(fù),潮起潮落,昨天和前天顛倒順序 也沒(méi)有任何不便,我不時(shí)想,這叫什么人生啊。 ...
    晨妤兒閱讀 550評(píng)論 0 2
  • 2017年9月24日 星期日 今晚看《80天環(huán)游地球》第35章,特別舒適的快車(chē)。 ??讼壬恍腥说巧狭舜┰矫?..
    鑫隆媽媽閱讀 296評(píng)論 0 0

友情鏈接更多精彩內(nèi)容