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

前言

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

但是 category 也有很多弊端~

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

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

舉一個簡單的例子:

兩個Class的分類:Demo+A取董、Demo+B

@implementation Demo (A)

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

@end
@implementation Demo (B)

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

@end

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

解決

因為OC的這個機制,我發(fā)現(xiàn)這塊太容易產(chǎn)生錯誤的懈贺,當(dāng)自己在多個分類寫代碼的時候经窖,太容易方法名重名了(更何況絕大部分時刻,你是拷貝的別人的代碼梭灿,就更容易了画侣,咳咳

為了避免這類事情發(fā)生,我查了相關(guān)資料并寫了一個腳本來靜態(tài)檢測一個類的分類是否有重名方法堡妒,技術(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ù)實現(xiàn)類的內(nèi)容進行檢查
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]
        # 可能有多個方法參數(shù)理盆,匹配每一個參數(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、方法校驗

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ù)實現(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é)束了速勇,你也可以私信我及時獲取最新資料以及面試相關(guān)資料晌砾。如果你有什么意見和建議歡迎給我留言坎拐。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市养匈,隨后出現(xiàn)的幾起案子哼勇,更是在濱河造成了極大的恐慌,老刑警劉巖呕乎,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件积担,死亡現(xiàn)場離奇詭異,居然都是意外死亡猬仁,警方通過查閱死者的電腦和手機帝璧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湿刽,“玉大人的烁,你說我怎么就攤上這事≌┕耄” “怎么了渴庆?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雅镊。 經(jīng)常有香客問我襟雷,道長,這世上最難降的妖魔是什么仁烹? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任耸弄,我火速辦了婚禮,結(jié)果婚禮上卓缰,老公的妹妹穿的比我還像新娘叙赚。我一直安慰自己老客,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布震叮。 她就那樣靜靜地躺著胧砰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苇瓣。 梳的紋絲不亂的頭發(fā)上尉间,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音击罪,去河邊找鬼哲嘲。 笑死,一個胖子當(dāng)著我的面吹牛媳禁,可吹牛的內(nèi)容都是我干的眠副。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼竣稽,長吁一口氣:“原來是場噩夢啊……” “哼囱怕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起毫别,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤娃弓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后岛宦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體台丛,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年砾肺,在試婚紗的時候發(fā)現(xiàn)自己被綠了挽霉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡变汪,死狀恐怖侠坎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情疫衩,我是刑警寧澤硅蹦,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站闷煤,受9級特大地震影響童芹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鲤拿,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一巍糯、第九天 我趴在偏房一處隱蔽的房頂上張望踏幻。 院中可真熱鬧自点,春花似錦、人聲如沸宁否。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慕匠。三九已至,卻和暖如春域醇,著一層夾襖步出監(jiān)牢的瞬間台谊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工譬挚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锅铅,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓减宣,卻偏偏與公主長得像盐须,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子漆腌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容