iOS AOP編程之路

1. AOP簡介

AOP: Aspect Oriented Programming 面向切面編程。

面向切面編程(也叫面向方面):Aspect Oriented Programming(AOP),是目前軟件開發(fā)中的一個熱點。利用AOP可以對業(yè)務(wù)邏輯的各個部分進行隔離收恢,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低擎颖,提高程序的可重用性,同時提高了開發(fā)的效率购裙。

AOP是OOP的延續(xù),是(Aspect Oriented Programming)的縮寫,意思是面向切面(方面)編程坯台。

主要的功能是:日志記錄,性能統(tǒng)計瘫寝,安全控制蜒蕾,事務(wù)處理,異常處理等等焕阿。

主要的意圖是:將日志記錄咪啡,性能統(tǒng)計,安全控制暮屡,事務(wù)處理撤摸,異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來,通過對這些行為的分離褒纲,我們希望可以將它們獨立到非指導(dǎo)業(yè)務(wù)邏輯的方法中准夷,進而改? 變這些行為的時候不影響業(yè)務(wù)邏輯的代碼。

可以通過預(yù)編譯方式和運行期動態(tài)代理實現(xiàn)在不修改源代碼的情況下給程序動態(tài)統(tǒng)一添加功能的一種技術(shù)莺掠。AOP實際是GoF設(shè)計模式的延續(xù)衫嵌,設(shè)計模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之間的解耦,AOP可以說也是這種目標的一種實現(xiàn)彻秆。

假設(shè)把應(yīng)用程序想成一個立體結(jié)構(gòu)的話楔绞,OOP的利刃是縱向切入系統(tǒng)论悴,把系統(tǒng)劃分為很多個模塊(如:用戶模塊,文章模塊等等)墓律,而AOP的利刃是橫向切入系統(tǒng)膀估,提取各個模塊可能都要重復(fù)操作的部分(如:權(quán)限檢查,日志記錄等等)耻讽。由此可見察纯,AOP是OOP的一個有效補充。

注意:AOP不是一種技術(shù)针肥,實際上是編程思想饼记。凡是符合AOP思想的技術(shù),都可以看成是AOP的實現(xiàn)

2. iOS中的AOP

利用 Objective-C 的 Runtime 特性慰枕,我們可以給語言做擴展具则,幫助解決項目開發(fā)中的一些設(shè)計和技術(shù)問題。這一篇具帮,我們來探索一些利用 Objective-C Runtime 的黑色技巧博肋。這些技巧中最具爭議的或許就是 Method Swizzling 。

其次蜂厅,用不用就看項目規(guī)模和團隊規(guī)模匪凡。有些業(yè)務(wù)確實非常適合使用AOP,比如log,AOP還可以用來debug

AOP的優(yōu)勢:

減少切面業(yè)務(wù)的開發(fā)量掘猿,“一次開發(fā)終生使用”病游,比如日志

減少代碼耦合,方便復(fù)用稠通。切面業(yè)務(wù)的代碼可以獨立出來衬衬,方便其他應(yīng)用使用

提高代碼review的質(zhì)量,比如我可以規(guī)定某些類的某些方法才用特定的命名規(guī)范改橘,這樣review的時候就可以發(fā)現(xiàn)一些問題

AOP的弊端:

它破壞了代碼的干凈整潔滋尉。

(因為 Logging 的代碼本身并不屬于 ViewController 里的主要邏輯。隨著項目擴大唧龄、代碼量增加兼砖,你的 ViewController 里會到處散布著 Logging 的代碼。這時既棺,要找到一段事件記錄的代碼會變得困難讽挟,也很容易忘記添加事件記錄的代碼)

3. iOS AOP實戰(zhàn)

玩轉(zhuǎn) Method Swizzling

1.事務(wù)攔截,安全可變?nèi)萜?/p>

iOS中有各類容器的概念丸冕,容器分可變?nèi)萜骱头强勺內(nèi)萜鞯⒚罚勺內(nèi)萜饕话銉?nèi)部在實現(xiàn)上是一個鏈表,在進行各類(insert 胖烛、remove眼姐、 delete诅迷、 update )難免有空操作、指針越界的問題众旗。

最粗暴的方式就是在使用可變?nèi)萜鞯臅r間罢杉,每次操作都必須手動做空判斷、索引比較這些操作

NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];

if (obj) {

[dic setObject:obj forKey:@"key"];

}

NSMutableArray *array = [[NSMutableArray alloc] init];

if (index < array.count) {

NSLog(@"%@",[array objectAtIndex:index]);

}

在代碼中大量的使用這鞋操作實在是太過于繁瑣了贡歧,試想如果可變?nèi)萜髯陨砣绾文茏鲞@些兼容豈不是更好滩租。可能會想到繼承的方法來解決利朵,但是項目中盡可能的避免過多的派生(至于派生的弊端這里就不多說了)律想;或者想到分類,這里也不盡人意绍弟。

Method Swizzling 移花接木

runtime 這里就不多多說了(swift里面已經(jīng)對這個概念的說法從心轉(zhuǎn)變成了 Reflection<反射>)技即,objective c中每個方法的名字(SEL)跟函數(shù)的實現(xiàn)(IMP)是一一對應(yīng)的,Swizzle的原理只是在這個地方做下手腳樟遣,將原來方法名與實現(xiàn)的指向交叉處理而叼,就能達到一個新的效果。

廢話少說年碘,直接上代碼:

這里使用NSMutableArray 做實例澈歉,為NSMutableArray追加一個新的方法

@implementation NSMutableArray (safe)

+ (void)load

{

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

id obj = [[self alloc] init];

[obj swizzleMethod:@selector(addObject:) withMethod:@selector(safeAddObject:)];

[obj swizzleMethod:@selector(objectAtIndex:) withMethod:@selector(safeObjectAtIndex:)];

[obj swizzleMethod:@selector(insertObject:atIndex:) withMethod:@selector(safeInsertObject:atIndex:)];

[obj swizzleMethod:@selector(removeObjectAtIndex:) withMethod:@selector(safeRemoveObjectAtIndex:)];

[obj swizzleMethod:@selector(replaceObjectAtIndex:withObject:) withMethod:@selector(safeReplaceObjectAtIndex:withObject:)];

});

}

- (void)safeAddObject:(id)anObject

{

if (anObject) {

[self safeAddObject:anObject];

}else{

NSLog(@"obj is nil");

}

}

- (id)safeObjectAtIndex:(NSInteger)index

{

if(index<[self count]){

return [self safeObjectAtIndex:index];

}else{

NSLog(@"index is beyond bounds ");

}

return nil;

}

- (void)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector

{

Class class = [self class];

Method originalMethod = class_getInstanceMethod(class, origSelector);

Method swizzledMethod = class_getInstanceMethod(class, newSelector);

BOOL didAddMethod = class_addMethod(class,

origSelector,

method_getImplementation(swizzledMethod),

method_getTypeEncoding(swizzledMethod));

if (didAddMethod) {

class_replaceMethod(class,

newSelector,

method_getImplementation(originalMethod),

method_getTypeEncoding(originalMethod));

} else {

method_exchangeImplementations(originalMethod, swizzledMethod);

}

}

這里唯一可能需要解釋的是 class_addMethod 展鸡。要先嘗試添加原 selector 是為了做一層保護屿衅,因為如果這個類沒有實現(xiàn) originalSelector ,但其父類實現(xiàn)了莹弊,那 class_getInstanceMethod 會返回父類的方法涤久。這樣 method_exchangeImplementations 替換的是父類的那個方法,這當然不是你想要的忍弛。所以我們先嘗試添加 orginalSelector 响迂,如果已經(jīng)存在,再用 method_exchangeImplementations 把原方法的實現(xiàn)跟新的方法實現(xiàn)給交換掉细疚。

safeAddObject 代碼看起來可能有點奇怪蔗彤,像遞歸不是么。當然不會是遞歸疯兼,因為在 runtime 的時候然遏,函數(shù)實現(xiàn)已經(jīng)被交換了。調(diào)用 objectAtIndex: 會調(diào)用你實現(xiàn)的 safeObjectAtIndex:吧彪,而在 NSMutableArray: 里調(diào)用 safeObjectAtIndex: 實際上調(diào)用的是原來的 objectAtIndex: 待侵。

如此以來,一直擔心的問題就迎刃而解了姨裸,不僅在可變數(shù)組秧倾、可變字典等容器內(nèi)都可以做自己想做的事情怨酝。



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市那先,隨后出現(xiàn)的幾起案子农猬,更是在濱河造成了極大的恐慌,老刑警劉巖售淡,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盛险,死亡現(xiàn)場離奇詭異,居然都是意外死亡勋又,警方通過查閱死者的電腦和手機苦掘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來楔壤,“玉大人鹤啡,你說我怎么就攤上這事《紫” “怎么了递瑰?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長隙畜。 經(jīng)常有香客問我抖部,道長,這世上最難降的妖魔是什么议惰? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任慎颗,我火速辦了婚禮,結(jié)果婚禮上言询,老公的妹妹穿的比我還像新娘俯萎。我一直安慰自己,他們只是感情好运杭,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布夫啊。 她就那樣靜靜地躺著,像睡著了一般辆憔。 火紅的嫁衣襯著肌膚如雪撇眯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天虱咧,我揣著相機與錄音熊榛,去河邊找鬼。 笑死彤钟,一個胖子當著我的面吹牛来候,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播逸雹,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼营搅,長吁一口氣:“原來是場噩夢啊……” “哼云挟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起转质,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤园欣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后休蟹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沸枯,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年赂弓,在試婚紗的時候發(fā)現(xiàn)自己被綠了绑榴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡盈魁,死狀恐怖翔怎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杨耙,我是刑警寧澤赤套,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站珊膜,受9級特大地震影響容握,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜车柠,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一剔氏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堪遂,春花似錦介蛉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽践险。三九已至猿妈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間巍虫,已是汗流浹背彭则。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留占遥,地道東北人俯抖。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像瓦胎,于是被迫代替她去往敵國和親芬萍。 傳聞我的和親對象是個殘疾皇子尤揣,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

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

  • layout: posttitle: "漫談iOS AOP編程之路 "subtitle: "...
    風(fēng)之痕_閱讀 29,422評論 11 109
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,682評論 0 9
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法柬祠,類相關(guān)的語法北戏,內(nèi)部類的語法,繼承相關(guān)的語法漫蛔,異常的語法嗜愈,線程的語...
    子非魚_t_閱讀 31,587評論 18 399
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,520評論 25 707
  • 網(wǎng)絡(luò)設(shè)備每一層為了安全起見,一般都會設(shè)置密碼的莽龟,但是如果忘記了怎么破蠕嫁,今天給大家講一下 一般密碼是保存在start...
    G木頭人閱讀 1,049評論 0 1