前言
在項(xiàng)目中孵户,我們經(jīng)常會(huì)使用分類 -> category槐壳。category在實(shí)際項(xiàng)目中一般有兩個(gè)左右:1.給已有class增加方法,擴(kuò)充起能力英遭、2.將代碼打散到多個(gè)文件中,避免因?yàn)橐粋€(gè)類過(guò)于復(fù)雜而導(dǎo)致代碼篇幅過(guò)長(zhǎng)(應(yīng)用于viewController中很好用)
但是 category 也有很多弊端~
首先:它不可以直接添加屬性(無(wú)法生成成員變量亦渗,需要使用對(duì)象關(guān)聯(lián)來(lái)協(xié)助添加屬性)
其次:當(dāng)多人協(xié)作開發(fā)項(xiàng)目時(shí)挖诸,一個(gè)class可能存在多個(gè)category,iOS項(xiàng)目編譯時(shí)央碟,是按照一定的順序來(lái)編譯文件(編譯順序和Compile Sources的文件順序相關(guān))税灌,此時(shí)如果兩個(gè)category實(shí)現(xiàn)了相同名字的方法均函,后編譯的category中的方法會(huì)將先編譯的category中相同名字的方法屏蔽,先編譯category的該方法永遠(yuǎn)不會(huì)被執(zhí)行~
舉一個(gè)簡(jiǎn)單的例子:
兩個(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)不會(huì)被執(zhí)行到0病!
(具體原因這里不做過(guò)多介紹粘秆,感興趣的同學(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è)腳本來(lái)靜態(tài)檢測(cè)一個(gè)類的分類是否有重名方法,技術(shù)的坑還是要靠技術(shù)解決廢話不多說(shuō)摘符,直接上源碼:
1贤斜、定義白名單
首先定義白名單,我定義了四種類型的白名單逛裤,分別是(文件白名單瘩绒、class白名單、方法白名單带族、文件夾白名單)锁荔,白名單中的成員不在檢測(cè)范圍之內(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):
# 白名單過(guò)濾
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且無(wú)參數(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é)識(shí)人脈!里面都是iOS開發(fā)换途,全棧發(fā)展懊渡,歡迎入駐刽射,共同進(jìn)步!(群內(nèi)會(huì)免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書籍資料以及整理好的幾百道面試題和答案文檔L曛础)
-
——點(diǎn)擊加入:iOS開發(fā)交流群
以下資料在群文件可自行下載
駐 誓禁,分享BAT,阿里面試題、面試經(jīng)驗(yàn)肾档,討論技術(shù)摹恰, 大家一起交流學(xué)習(xí)成長(zhǎng)!**