iOS基礎之block

1.block類型-存儲代碼塊的類型

在異步編程時常需要進行函數回調,在C#中會用匿名委托或者lambda表達式講一個操作作為參數進行傳遞.
ObjC中是使用對于閉包的實現,在塊狀中我們可以持有或引用局部變量. 同時利用Block可以將一個操作作為參數進行傳遞;

blcok用法:

  • 定義:返回值類型 ( ^變量名 ) ( 形參類型 );
  • 賦值:變量名=^(形參){
    代碼塊+形參變量
    };
  • 使用:變量(實參);

例:

    int (^myBlcok)(int ,int)=^(int m,int n){
      return  m+n;
      }; //無參數時大括號前()可省略
      
      myBlock(10,5);    //調用塊,省略了接受塊返回值;

總結:經過簡單了解C與OC;發現從最小的一個變量到表達式再到一個函數,其實只起兩點作用: 值(返回值) 與 功能(行為,方法,作用).
所以說一行代碼,按它是使用了值 還是 功能來解讀比較容易理解.

Block做使用場景:

  • 如果回調方法比較少,1~2,最好不要超過3個,這個時候使用block比較合適
  • 如果回調方法非常多,同時又不用每一個方法都必須實現,這個時候用delegate會比較方便!

block傳值的循環引用問題:

只有當block直接或間接的被self持有時,在block使用self時才需要替換為weak self。如果在 Block 內需要多次 訪問 self,則需要使用 strongSelf。

   __weak __typeof__(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        __strong __typeof(self) strongSelf = weakSelf;

        [strongSelf doSomething];

        [strongSelf doOtherThing];

    });

typedef block格式

類似函數指針,直接在定義格式之前加 typedef關鍵字,之后變量名就是類型的別名了.
typedef viod (^別名)(形參);
一般以后需要使用block作為函數方法的參數時,為方便最好用別名.而在block作用返回值時,一定需要別名,因為編譯器不能識別做此時類型做何種解釋.
延伸:經過測試,block在編譯時按代碼順序,而運行時按調用順序(變量作用域)

使用例子:
KCButton.h

#import <Foundation/Foundation.h>
@class KCButton;
typedef void(^KCButtonClick)(KCButton *);

@interface KCButton : NSObject

#pragma mark - 屬性
@property (nonatomic,copy) KCButtonClick onClick;

#pragma mark 點擊方法
-(void)click;
@end

KCButton.m

#import "KCButton.h"


@implementation KCButton

-(void)click{
    NSLog(@"Invoke KCButton's click method.");
    if (_onClick) {
        _onClick(self);
    }
}

@end

main.m

   KCButton *button=[[KCButton alloc]init];
    button.onClick=^(KCButton *btn){
        NSLog(@"Invoke onClick method.The button is:%@.",btn);
    };
    [button click];
    /*結果:
     Invoke KCButton's click method.
     Invoke onClick method.The button is:<KCButton: 0x1006011f0>.
     */

block訪問外部變量

  • block內部可以訪問外部局部變量,但是此時是const copy方式,地址不同,相當于值傳遞,只讀的.如果外部定義時加前綴__block時,內部可改變外部局變值.
  • block內部如果創建了和外部同名的變量,會屏蔽外部作用域.此時內部的變量也存在棧區;
    原因:block本質是代碼塊,ARC下創建的時候在堆區,此時代碼只是單純儲存,沒有功能;當調用的時候,相當于代碼增加到main中,這樣代碼塊中創建的變量就跟正常的一樣; (block調用完成內部變量即釋放,而堆區的只在釋放block時一起釋放).
  • 如果是靜態變量(static修飾局變,生命周期延長,存儲在數據區(同初始化的全局))和全局變量.地址傳遞.此時block存儲在全局區.
  • 常量字符串@"abc",加__block會引用常量變量(如:a變量,a = @"abc",內部可以任意修改a 指向的內容)的地址。不加block就是@"abc"本身地址,不可變;

三種類型block

根據block在內存中的位置
"NSGlobaBlock"類似函數,存于代碼區--全局block
"NNStackBlock"棧區,函數返回后的Block--棧
"NSMallocBlock"堆block--堆
  1. block內沒有使用外部變量或是只使用了全局/靜態變量時.存于全局代碼區,為全局block;---(ARC和MRC下一致)
  2. 當使用外部變量時
    • MRC下,block代碼存于棧區;如果此外部變量A存于區,那么A會被copy到block分配的區;如果A是存于區,那么A在block塊內與快外相同.
    • ARC下,block代碼存于堆區.如果此外部變量A存于區,那么A會被copy到block分配的區;如果A是存于區,那么A在block塊內與快外相同.
  3. 如果需要修改外部變量,需要在變量前面聲明__Block;
    當使用下劃線Block修飾外部變量時:
    • MRC下,無論變量A存于還是區,A在block塊內與快外相同;
    • ARC下,如果此外部變量A存于區,那么A會被轉移而不是復制區;如果A是存于區,那么A在block塊內與快外相同.

面試題:block的@property參數(內存管理參數)為什么要用copy:如果不用copy,此時不論ARC還是MRC都是棧Bolck,棧block會提前釋放,導致無法繼續使用;可以copy到堆區手動管理內存.(而字符串copy是防止字符串如果是非常量的,外部可變,造成非預估的結果;)

block在MRC下得內存隱患(NNStacKBlock)

Block_copy將block及內部變量拷貝到堆區.
使用完畢用Blok_release(block變量)釋放此堆區空間;


block使用技巧

  1. block結構快速顯示:inlineBlock...(也可右下角自定義快速顯示其他格式)
  2. Block作為方法參數時,最好把參數列表部分加上,這樣后面調用方法時,會自動有格式;
  3. 做方法參數時,需要加上返回值類型;
  4. 做返回值時,先定義別名,最后別忘記執行返回值;
  5. 方法中,void(^)()表示block類型同int,做參和返回值;做實例變量
    @property (nonatomic, copy) void(^變量名)()
  6. get點語法獲取block類型實例變量時,自動執行,后面需加();
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 Blocks是C語言的擴充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,787評論 0 23
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,217評論 30 472
  • 《Objective-C高級編程》這本書就講了三個東西:自動引用計數、block、GCD,偏向于從原理上對這些內容...
    WeiHing閱讀 9,907評論 10 69
  • 1.項目經驗 2.基礎問題 3.指南認識 4.解決思路 ios開發三大塊: 1.Oc基礎 2.CocoaTouch...
    陽光的大男孩兒閱讀 5,042評論 0 13
  • tl,drWindows系統環境下gvim和vim的中文支持是不相同的。一般gvim按如下配置即可保證中文支持沒有...
    mst7閱讀 6,142評論 0 2