讀“編寫高質量iOS與OSX代碼的52個有效方法”筆記(04)

塊和大中樞派發(fā)

第37條:理解”塊“這一概念

Block的了解還是很有必要的屁桑,它和函數類似拯爽,只不過是直接定義在另一個函數里的最蕾,和定義它的那個函數共享同一個范圍的東西。用^來表示源内,后面接一對花括號,括號里是blcok 的實現代碼份殿。

void(^someBlcok)() = ^{

    NSLog(@"there has someBlock");

};

someBlcok();  // print:“there has someBlock”

格式: 返回類型 (^Block Name)(參數){};

int (^addBlock)(int a, int b) = ^(int a ,int b){
    
    return a + b;
    
};
int testAdd = addBlock(7,5);
NSLog(@"testAdd === %d",testAdd); // print: “testAdd === 12”


int additonal = 5;
int (^minusBlock)(int a, int b) = ^(int a,int b){
    
    return  additonal -  a - b;
};
int testMinus = minusBlock(2,6);
NSLog(@"minus === %d",testMinus);  //print :"minus === -3"

注意當要改變塊里面的變量的時候膜钓,需要加上__block修飾符

NSArray * array = @[@0,@1,@2,@3,@4,@5];
__block NSInteger count = 0;
[array enumerateObjectsUsingBlock:^(NSNumber * number,NSUInteger idx,BOOL *stop){
    if([number compare:@4] == NSOrderedAscending)
    {
        count++;
    }
    
}];

NSLog(@"count ==== %lu",count);  // print : "count ==== 4"
注意還可以使用反序和順序
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts 
                         usingBlock:(void (^)(ObjectType obj, NSUInteger idx, BOOL *stop))block

typedef NS_OPTIONS(NSUInteger, NSEnumerationOptions) {
    NSEnumerationConcurrent = (1UL << 0), // 順序
    NSEnumerationReverse = (1UL << 1), // 倒序
};

定義block的時候,所占的內存區(qū)域是分配在棧中的卿嘲,也就是block只在定它的那個范圍內有效颂斜。所以我們我們需要對其對象發(fā)送copy消息以拷貝值,這樣的話拾枣,就可以把塊從棧復制到堆了沃疮,塊這樣就成了帶引用計數的對象啦。

第38條:為常用的塊類型創(chuàng)建typedef

由于在定義塊變量時梅肤,需要把變量名放在類型之中司蔬,而不要放在右側,這樣非常難記姨蝴,也非常難懂俊啼,鑒于此,我們應該為常用的塊類型起個別名左医,此時typedef關鍵字就用到了授帕。

typedef int (^YPQBlcok)(BOOL flag,int value);
第39條:用handler塊降低代碼分散程度

當用Handler塊的時候,可以直接將塊和相關對象放在一起浮梢。這樣代碼更清晰而緊湊跛十。

#import <Foundation/Foundation.h>

typedef void(^YPQNetworkCompletionHandler)(NSData * data);

@interface YPQNetworkFetcher: NSObject

@property (nonatomic, readonly, strong) NSURL * url;

- (id)initWithURL:(NSURL *)url;
- (void)startWithCompletionHandler:(YPQNetworkCompletionHandler)comletion;

@end

注意實現文件中 的 copy

#import "YPQNetworkFetcher.h"

@interface YPQNetworkFetcher()

@property (nonatomic, readwrite, strong)NSURL * url;
@property (nonatomic, copy)YPQNetworkCompletionHandler completionHandle;
@property (nonatomic, strong) NSData * downloadedData;

@end

@implementation YPQNetworkFetcher

- (id)initWithURL:(NSURL *)url
{
    if(self = [super init])
    {
        _url = url;
    }
    return self;
}
- (void)startWithCompletionHandler:(YPQNetworkCompletionHandler)comletion
{
    self.completionHandle = comletion;
    // 開始 request
    // downloadedData 獲值
    // 當申請完成,調用 [self p_requestCompleted];
   
}

- (void)p_requestCompleted
{
    if(_completionHandle)
    {
        _completionHandle(_downloadedData);
    }
}


@end
第40條:用塊引用其所屬對象時不要出現保留環(huán)

就是我們使用Block的時候很容易出現循環(huán)引用秕硝。

#import "ViewController.h"
#import "YPQNetworkFetcher.h"

@interface ViewController ()
{
    YPQNetworkFetcher * _networkFetcher;
    
}
@property (nonatomic ,strong) NSData * downloadData;

@end


- (void)downloadTheData
{
    NSURL * url = [NSURL URLWithString:@"https://www.example.com/...."];
    _networkFetcher = [[YPQNetworkFetcher alloc] initWithURL:url];
    [_networkFetcher startWithCompletionHandler:^(NSData * data){
       self.downloadData = data;
    }];
    
}

這樣看起來沒問題芥映,但是已經形成循環(huán)引用啦

循環(huán)引用
  1. block保留了viewController實例;
  2. viewController實例則通過_networkFetcher保留YPQNetworkFetcher
  3. _completionHandle又保留了block屏轰。

這樣環(huán)就形成了颊郎,打破環(huán)就得打破其中某一個環(huán)節(jié),比較好用的方法可以通過;

// 打破上述的第一環(huán)節(jié)
__weak __typeof(self) weakSelf = self;
 [_networkFetcher startWithCompletionHandler:^(NSData * data){
   weakSelf.downloadData = data;
}];

或者

// 打破上述的第二環(huán)節(jié)
[_networkFetcher startWithCompletionHandler:^(NSData * data){

   self.downloadData = data;
    _networkFetcher = nil;
   
}];
第41條:多用派發(fā)隊列霎苗,少用同步鎖

如果多個線程要執(zhí)行同一份代碼的時候姆吭,通常都要使用鎖實現某種同步機制。

 @synchronized(self) {
    //safe
}

_lock = [[NSLock alloc] init];
[_lock lock];
//safe
[_lock unlock];

但是這兩種方式效率不高唁盏,而且也無法提供絕對的線程安全内狸。有種簡單而高效的辦法可以代替他們,那即是使用”串行同步隊列“厘擂,將讀取操作和寫入操作都安排在同一個隊列中昆淡。

_syncQuene = dispatch_queue_create("com.yang.test", NULL); - (NSString *)testGCDString
{
      __block NSString * localString;
      dispatch_sync(_syncQuene, ^{
           localString = _testGCDString;
      });
      return localString;
}

- (void)setTestGCDString:(NSString *)testGCDString
{
       dispatch_sync(_syncQuene, ^{
          _testGCDString = testGCDString;
       });
}

當然還可以繼續(xù)優(yōu)化, 將同步派發(fā)改為異步派發(fā),并且由于多個獲取方法可以并發(fā)執(zhí)行刽严,而獲取方法和設置方法之間不能并發(fā)執(zhí)行昂灵,此時改用并發(fā)隊列。

_syncQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- (NSString *)testGCDString
{
      __block NSString * localString;
      dispatch_sync(_syncQuene, ^{
           localString = _testGCDString;
      });
      return localString;
}

- (void)setTestGCDString:(NSString *)testGCDString
{
       dispatch_async(_syncQuene, ^{
          _testGCDString = testGCDString;
       });
}

但是像上面這樣舞萄,還是無法正確實現同步眨补。讀取和寫入可以隨時執(zhí)行,為了不讓其任意執(zhí)行倒脓,此時dispatch_barrier_async就出現啦

_syncQuene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- (NSString *)testGCDString
{
    __block NSString * localString;
    dispatch_sync(_syncQuene, ^{
        
        localString = _testGCDString;
    });
    return localString;

}

- (void)setTestGCDString:(NSString *)testGCDString
{
    dispatch_barrier_async(_syncQuene, ^{
        
        _testGCDString = testGCDString;
    });
}

dispatch_barrier_async必須單獨執(zhí)行撑螺,不能與其他塊并行。這只對并發(fā)隊列有意義崎弃,因為串行隊里中塊總是按順序逐個來執(zhí)行的甘晤。并發(fā)隊列如果發(fā)現接下來要處理的塊是個barrier block,那么就一直要等到當前所有并發(fā)塊都執(zhí)行完畢,才會單獨執(zhí)行這個barrier block饲做。待barrier block執(zhí)行過后线婚,再按正常方式繼續(xù)向下處理。

在上面這個隊列中艇炎,在寫入操作用了dispatch_barrier_async來實現后酌伊,對屬性的讀取操作依然可以并行,但寫入操作必須單獨執(zhí)行缀踪。當然設置函數中居砖,我們也可以用dispatch_barrier_sync同步來實現,有時可能更高效驴娃,看具體場景吧奏候。

將同步與異步派發(fā)結合起來,實現與普通加鎖機制一樣的同步行為唇敞,也不會阻塞執(zhí)行異步派發(fā)的線程蔗草,所以是 OK 的咒彤。

第42條:多用GCD,少用performSelector

在我們用到推遲執(zhí)行方法的時候咒精,我們可以有種選擇镶柱。

// performSelector
 [self performSelector:@selector(afterThreeSecondBeginAction)
            withObject:nil
            afterDelay:3.0f];
// dispatch after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^(void){

    [self afterThreeSecondBeginAction];

});

performSelector系列方法在內存管理方面容易疏失,并且所處理的選擇子太過局限了模叙,GCD 作為純C的API歇拆,一般擔憂還是沒必要的,所以說少用performSelector還是有理由的范咨。

第43條:掌握GCD及操作隊列的使用時機

簡單的說故觅,不要過度使用GCD,要合理使用它渠啊,這個可能需要項目經驗的積累的输吏。像NSOperationQueue類也可以多了解下。

第44條:通過Dispatch Group 機制,根據系統(tǒng)資源狀況執(zhí)行任務

dispatch group是GCD的一項特性替蛉,能夠把任務分組贯溅。

這里是GCD的詳細介紹,推薦一篇文章@ 少君的GCD

第45條:使用dispatch_once 來執(zhí)行只需要運行一次的線程安全代碼
static id _instace;
+ (instancetype)sharedInstanceWithSome
{
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
           _instace = [[self alloc] init];
      });
      return _instace;
}

使用 dispatch_once可以簡化代碼并且徹底保證線程安全灭返,我們根本無須擔心加鎖或同步盗迟,另外它沒有使用重量級的同步機制,所以也更高效熙含。

第46條:不要使用dispatch_get_current_queue

其實這個已經在iOS6.0之后被棄用了,它可以做調試艇纺,但實際也用的不多怎静,所以一般我想應該是不會用到吧。

持續(xù)筆記中····

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末黔衡,一起剝皮案震驚了整個濱河市蚓聘,隨后出現的幾起案子,更是在濱河造成了極大的恐慌盟劫,老刑警劉巖夜牡,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異侣签,居然都是意外死亡塘装,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門影所,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蹦肴,“玉大人,你說我怎么就攤上這事猴娩∫趸希” “怎么了勺阐?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長矛双。 經常有香客問我渊抽,道長,這世上最難降的妖魔是什么议忽? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任懒闷,我火速辦了婚禮,結果婚禮上徙瓶,老公的妹妹穿的比我還像新娘毛雇。我一直安慰自己,他們只是感情好侦镇,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布灵疮。 她就那樣靜靜地躺著,像睡著了一般壳繁。 火紅的嫁衣襯著肌膚如雪震捣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天闹炉,我揣著相機與錄音蒿赢,去河邊找鬼。 笑死渣触,一個胖子當著我的面吹牛羡棵,可吹牛的內容都是我干的。 我是一名探鬼主播嗅钻,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼皂冰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了养篓?” 一聲冷哼從身側響起秃流,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柳弄,沒想到半個月后舶胀,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡碧注,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年嚣伐,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片应闯。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡纤控,死狀恐怖,靈堂內的尸體忽然破棺而出碉纺,到底是詐尸還是另有隱情船万,我是刑警寧澤刻撒,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站耿导,受9級特大地震影響声怔,放射性物質發(fā)生泄漏。R本人自食惡果不足惜舱呻,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一醋火、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧箱吕,春花似錦芥驳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怎栽,卻和暖如春丽猬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背熏瞄。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工脚祟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人强饮。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓由桌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親邮丰。 傳聞我的和親對象是個殘疾皇子沥寥,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內容