iOS 項(xiàng)目避坑:多個(gè)分類中方法重復(fù)實(shí)現(xiàn)檢測

前言

在項(xiàng)目中,我們經(jīng)常會使用分類 -> category。category在實(shí)際項(xiàng)目中一般有兩個(gè)左右:1.給已有class增加方法,擴(kuò)充起能力、2.將代碼打散到多個(gè)文件中,避免因?yàn)橐粋€(gè)類過于復(fù)雜而導(dǎo)致代碼篇幅過長(應(yīng)用于viewController中很好用)

但是 category 也有很多弊端~

首先:它不可以直接添加屬性(無法生成成員變量,需要使用對象關(guān)聯(lián)來協(xié)助添加屬性)

其次:當(dāng)多人協(xié)作開發(fā)項(xiàng)目時(shí),一個(gè)class可能存在多個(gè)category,iOS項(xiàng)目編譯時(shí),是按照一定的順序來編譯文件(編譯順序和Compile Sources的文件順序相關(guān)),此時(shí)如果兩個(gè)category實(shí)現(xiàn)了相同名字的方法,后編譯的category中的方法會將先編譯的category中相同名字的方法屏蔽,先編譯category的該方法永遠(yuǎn)不會被執(zhí)行~

舉一個(gè)簡單的例子:

兩個(gè)Class的分類:Demo+A、Demo+B

@implementation Demo (A)

- (void)test{
    NSLog(@"A");
}

@end
@implementation Demo (B)

- (void)test{
    NSLog(@"B");
}

@end

這種情況Demo+A中的test方法永遠(yuǎn)不會被執(zhí)行到??!
(具體原因這里不做過多介紹,感興趣的同學(xué)可以自己查看category的底層實(shí)現(xiàn)原理)

解決

因?yàn)镺C的這個(gè)機(jī)制,我發(fā)現(xiàn)這塊太容易產(chǎn)生錯(cuò)誤的,當(dāng)自己在多個(gè)分類寫代碼的時(shí)候,太容易方法名重名了(更何況絕大部分時(shí)刻,你是拷貝的別人的代碼,就更容易了,咳咳

為了避免這類事情發(fā)生,我查了相關(guān)資料并寫了一個(gè)腳本來靜態(tài)檢測一個(gè)類的分類是否有重名方法,技術(shù)的坑還是要靠技術(shù)解決廢話不多說,直接上源碼:

1、定義白名單
首先定義白名單,我定義了四種類型的白名單,分別是(文件白名單、class白名單、方法白名單、文件夾白名單),白名單中的成員不在檢測范圍之內(nèi)

# 文件名白名單,格式:xxx.m
file_white_list = []

# class 白名單,格式:xxx
class_white_list = []

# 方法名白名單,格式:(+/-)xxx(:xxx:xxx:)
method_white_list = ['+load', '-.cxx_destruct']

# 文件夾白名單,格式:xxx
dir_white_list = []
2、遍歷目錄下所有文件路徑
# 遍歷目錄下所有文件路徑
def find_file(rootDir):
    # 獲取路徑下包含的文件或文件夾的名字的列表
    dirs = os.listdir(rootDir)
    for file in dirs:
        path = os.path.join(rootDir, file)
        file_name = path.split('/')[-1]
        file_type = file_name.split('.')[-1]
        if file_type == 'm' or file_type == 'mm':
            read_file(path)
        # 判斷該文件類型是文件夾
        if os.path.isdir(path):
            # 白名單過濾
            if file_name in dir_white_list:
                continue
            find_file(path)

3、遍歷讀取文件內(nèi)容

def read_file(path):
    f = open(path)
    # 讀取文件內(nèi)容
    file_string = f.read()
    f.close()
    find_category_same_method(path, file_string)

4、正則匹配獲取class名字

def find_category_same_method(file, file_string):
    try:
        file_name = os.path.basename(file).strip()
    except Exception as e:
        print 'error: ' + str(e)
        pass
    if file_name in file_white_list:
        return

    # 正則提取 implementation 中內(nèi)容,獲取類名
    imple_regex = r'@implementation(.*?)@end'
    for imple_string in re.findall(imple_regex, file_string, re.S):
        class_name = imple_string.split('\n')[0]
        find_implementation_same_method(file, class_name, imple_string)

5、正則匹配獲取方法名字(生成格式:-/+方法名:)

# 根據(jù)實(shí)現(xiàn)類的內(nèi)容進(jìn)行檢查
def find_implementation_same_method(file_path, class_name, imple_string):
    # 匹配OC的方法名,以{結(jié)束
    func_regex = r'(\+|\-)\s*\([^;<>=\+\-]*?\)\s*([^;<>=\+\-]*?)\s*\{'
    function_list =  re.findall(func_regex, imple_string, re.S)
    for function in function_list:
        method_type = function[0]
        method = function[-1]
        # 可能有多個(gè)方法參數(shù),匹配每一個(gè)參數(shù)
        split_regex = r'(\w*?)\s*:\s*\(.*?\)'
        result = ""
        sub_methods = re.findall(split_regex, method, re.S)
        if len(sub_methods) == 0:
            result = method
        for sub_method in sub_methods:
            if len(sub_method) > 0:
                result = result + sub_method + ":"
        # 適配返回block且無參數(shù)的情況,如:- (void(^)(NSString *))func
        if result.find('(') != -1 and result.rfind(')') != -1:
            result_left = result[:result.find('(')]
            result_right = result[result.rfind(')') + 1:]
            result = result_left + result_right
        result = method_type + result
        handle_method(file_path, class_name, result)

6、方法校驗(yàn)

def handle_method(file_path, class_name, method_name):

    # @implementation xxx { ... }
    if class_name.find('{') != -1:
        class_name = class_name[:class_name.find('{')]

    # @implementation xxx (Category)
    if class_name.find('(') != -1:
        class_name = class_name[:class_name.find('(')]

    # 去除空格
    class_name = class_name.strip()
    class_name = class_name.rstrip()

    # 排除一些implementation
    if class_name in class_white_list:
        return

    # 排除一些方法名
    if method_name in method_white_list:
        return
    method_list = {}
    if method_dict.has_key(class_name):
        method_list = method_dict[class_name]
        if method_list.has_key(method_name) and method_list[method_name] != file_path:
            print("\n#####################\n")
            print 'Class:' + class_name + ' 方法名: [' + method_name + '] 重復(fù)實(shí)現(xiàn) \n 路徑一:' + file_path + ' \n 路徑二:' + method_list[method_name] 
            print("\n#####################\n")
        method_list[method_name] = file_path
    else:
        method_list[method_name] = file_path
    method_dict[class_name] = method_list

7、入口方法及使用

if __name__ == '__main__':
    folder_path_list = sys.argv[1:]
    print folder_path_list
    for path in folder_path_list:
        find_file(path)

python Python文件名.py 文件夾路徑

執(zhí)行結(jié)果:

結(jié)交人脈

最后推薦個(gè)我的iOS交流群:789143298
'有一個(gè)共同的圈子很重要,結(jié)識人脈!里面都是iOS開發(fā),全棧發(fā)展,歡迎入駐,共同進(jìn)步?。ㄈ簝?nèi)會免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書籍資料以及整理好的幾百道面試題和答案文檔!)

  • ——點(diǎn)擊加入:iOS開發(fā)交流群
    以下資料在群文件可自行下載

    駐 ,分享BAT,阿里面試題、面試經(jīng)驗(yàn),討論技術(shù), 大家一起交流學(xué)習(xí)成長!**
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 前言 使用 Category 為已經(jīng)存在的類添加方法是我們很熟悉的常規(guī)操作,但是如果在 Category 中為類添...
    編程怪才_凌雨畫閱讀 802評論 0 1
  • 來源作者:字節(jié)跳動技術(shù)團(tuán)隊(duì) 前言啟動是 App 給用戶的第一印象,啟動越慢用戶流失的概率就越高,良好的啟動速度是用...
    iOS弗森科閱讀 1,812評論 0 28
  • 久違的晴天,家長會。 家長大會開好到教室時(shí),離放學(xué)已經(jīng)沒多少時(shí)間了。班主任說已經(jīng)安排了三個(gè)家長分享經(jīng)驗(yàn)。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,788評論 16 22
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    余生動聽閱讀 10,798評論 0 11
  • 可愛進(jìn)取,孤獨(dú)成精。努力飛翔,天堂翱翔。戰(zhàn)爭美好,孤獨(dú)進(jìn)取。膽大飛翔,成就輝煌。努力進(jìn)取,遙望,和諧家園??蓯塾巫?..
    趙原野閱讀 3,443評論 1 1

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