你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS 開發 block深入淺出詳解

iOS 開發 block深入淺出詳解

編輯:IOS開發綜合

block的定義與使用

/*
*1.最簡單的定義方式:
*格式:void (^myBlock)() = ^ { // 代碼實現; }
*/
void (^myBlock)() = ^ {
    NSLog(@"hello");
};

// 執行時,把block當成函數
myBlock();

/*
*2.定義帶參數的block:
*格式:void (^block名稱)(參數列表) = ^ (參數列表) { // 代碼實現; }
*/
void (^sumBlock)(int, int) = ^ (int x, int y) {
    NSLog(@"%d", x + y);
};

sumBlock(10, 20);

/*
*3.定義帶返回值的block
*格式:返回類型 (^block名稱)(參數列表) = ^ 返回類型 (參數列表) { // 代碼實現; }
*/
int (^sumBlock2)(int, int) = ^ int (int a, int b) {
    return a + b;
};

NSLog(@"%d", sumBlock2(4, 8));

block 指針

//聲明一個名字為square的Block Pointer,其所指向的Block有一個int輸入和int輸出  
int (^square)(int);  

//block 指針square的內容
square = ^(int a){ return a*a ; };  

//調用方法,感覺是是不是很像function的用法?  
int result = square(5);  
NSLog(@"%d", result); 

用typedef先聲明類型,再定義變量進行賦值

typedef int (^MySum)(int,int);
MySum sum = ^(int a,int b)
 { 
      return a + b;
};

block 訪問外部變量

block內部可以訪問外部變量; 默認情況下block內部不能修改外面的局部變量; 給局部變量加上關鍵字_block,這個局部變量就可以在block內部修改;
int sum = 10;
?int (^MyBlock)(int) = ^(int num) 
{ 
     sum++;//編譯報錯 
     return num * sum; 
};

但是block使用有個特點,Block可以訪問局部變量,但是不能修改:

//如果要修改就要加關鍵字 __block (下面詳細說明):
__block int sum =10;

ARC環境:block存儲類型和訪問修改外部變量

前提是ARC : Block訪問外部的變量

1.ARC環境下,單純的定義一個block存儲在全局區 <NSGlobalBlock: 0x1045c60b0>

2.ARC環境下,block訪問外部的變量時這個block存儲在堆區 <NSMallocBlock: 0x7fa8fd00c1b0>

2.1 在block訪問這個變量之前,變量在棧區 == 0x7fff53c73bfc
2.2 在block內部訪問這個變量時,變量會被block拷貝到堆區 0x7fe851517880

前提是ARC : Block修改外部的變量

1.ARC環境下,當block修改外部變量的時候,會在堆區 <NSMallocBlock: 0x7f82ac8a6130>

2.在block的外面,即使你使用__block修飾了,那麼他的地址依然不變,在棧區 0x7fff5e101bf8

3.在block內部修改外部的變量時,使用__block修飾了外部的變量之後,外部的變量會在堆區 0x7f9d10e0bcc8

#pragma mark - ARC - Block修改外部的變量
// 需求 : 研究block和外部變量的內存的變化
- (void)blockDemo2 {
    __block int num = 10;

    // 在block的外面,即使你使用__block修飾了,那麼他的地址依然不變,在棧區 0x7fff5e101bf8
    NSLog(@"num01==%p",&num);

    void(^task2)() = ^{

        // 在block內部修改外部的變量時,使用__block修飾了外部的變量之後,外部的變量會在堆區 0x7f9d10e0bcc8
        NSLog(@"task2 %d =%p",num,&num);

        /*
         提示 : 在block內部修改外部變量是不被允許的
         如果非要修改,那麼久需要把外部的變量用 __block 來修飾
         */
        num = 20;
    };

    task2();

    // 當block修改外部變量的時候,會在堆區 <__NSMallocBlock__: 0x7f82ac8a6130>
    NSLog(@"%@",task2);

    // 當block內部修改完外部的變量之後,那麼這個變量的就會保存到堆區 0x7f9cd1708ca8
    NSLog(@"num02==%p",&num);
}

#pragma mark - ARC - Block訪問外部的變量
// 需求 : 研究block和外部變量的內存的變化
- (void)blockDemo1 {
    int num = 10;
    // 在block訪問這個變量之前,變量在棧區 == 0x7fff53c73bfc
    NSLog(@"num01==%p",&num);

    void(^task1)() = ^{
        // 在block內部訪問這個變量時,變量會被block拷貝到堆區 0x7fe851517880
        NSLog(@"task1 %d %p",num,&num);
    };

    task1();

    // block的本質是指針對象
    // ARC環境下,block訪問外部的變量時存儲在堆區 <__NSMallocBlock__: 0x7fa8fd00c1b0>
    NSLog(@"task1==%@",task1);

    // 當block在其內部使用完了外部的變量之後,這個變量又會重新回到棧區 0x7fff5a2d8bfc
    NSLog(@"num02==%p",&num);
}

- (void)blockDemo
{
    void(^task1)() = ^{
        NSLog(@"task");
    };

    task1();

    // block的本質是指針對象
    // ARC環境下,單純的定義一個block存儲在全局區 <__NSGlobalBlock__: 0x1045c60b0>
    NSLog(@"task1==%@",task1);
}

MRC環境:block存儲類型和訪問修改外部變量

前提是MRC :
1>MRC下,如果是單純的block,在常量區
2> MRC下,如果訪問或者修改外部變量,這個block在棧區,
訪問外部變量是,外部變量依然在棧區 0x7fff54accb28 (地址變化了,內存空間沒變)
修改外部變量時,外部變量的地址沒有發生變化
- (void)blockDemo3 {
    __block int num = 10;

    // 在棧區 0x7fff576d0b38
    NSLog(@"num01==%p",&num);

    void (^task3)() = ^{
        num = 20;

        // 在棧區 0x7fff576d0b38
        NSLog(@"task3 %d %p",num,&num);
    };

    task3();

    // 在棧區  <__NSStackBlock__: 0x7fff576d0ae0>
    NSLog(@"%@",task3);

    // 在棧區 0x7fff576d0b38
    NSLog(@"num02==%p",&num);
}

- (void)blockDemo2 {
    int num = 10;

    // 棧區 0x7fff54accb3c
    NSLog(@"num01==%p",&num);

    void(^task2)() = ^{

        // 依然在棧區 0x7fff54accb28 (地址變化了,內存空間沒變)
        NSLog(@"task2 %d %p",num,&num);
    };
    task2();

    // 存儲在棧區 <__NSStackBlock__: 0x7fff5670eb08>
    NSLog(@"%@",task2);

    // 棧區 0x7fff54accb3c
    NSLog(@"num02==%p",&num);
}

- (void)blockDemo1
{
    void(^task1)() = ^{
    };

    task1();

    // 全局/常量區 <__NSGlobalBlock__: 0x10c11a070>
    NSLog(@"%@",task1);
}

block為什麼要用copy修飾

// 定義一個塊代碼的屬性,block屬性需要用 copy
@property (nonatomic, copy) void (^completion)(NSString *text);

雖然在ARC時代已經不需要再顯式聲明了,使用strong是沒有問題的,但是仍然建 議我們使?copy以顯示相關拷貝?為

默認情況下,block是存檔在棧中,可能被隨時回收,通過copy操作可以使其在堆中保留一份, 相當於一直強引用著, 因此如果block中用到self時, 需要將其弱化, 通過__weak或者__unsafe_unretained. 以下是示例代碼及其說明, 讀者可以試著打印出不同情況下block的內存情況

堆區中的block,也就是copy修飾的block,生命周期隨著對象的銷毀而結束(引用計數變為0),只要引用block的對象不銷毀,就可以調用存放在堆區的block,這就是copy修飾block的原因

NSConcreteStackBlock(棧區block),需要涉及到外界變量的block在創建的時候是在stack上?分配空間的,也就是?旦所在函數返回,執行彈棧,則會被摧毀。這就導致內存管理的問題,如果我們希望保存這個block或者是返回它,如果沒有做進?步的copy處理,則必然會出現問題

在Objective-C語?言中,?一共有3種類型的block:
_NSConcreteGlobalBlock 全局的靜態block,不會訪問任何外部變量。
_NSConcreteStackBlock 保存在棧中的block,當函數返回時會被銷毀。
_NSConcreteMallocBlock 保存在堆中的block,當引?用計數為0時會被銷毀。

block引用外部變量時為什麼要用__block修飾

堆區的block有一下現象:block內部如果訪問外部聲明的對象,默認block內部會自動產生一個強指針強引用所指向的這個對象; 如果被訪問的外部對象被弱指針修飾,那麼block在內部引用的時候,就會自動產生一個弱指針指向所應用的對象

block解除循環引用

OC對象,不同於基本類型,Block會引起對象的引用計數變化。若我們在block中引用到oc的對象,則對象的引用計數器會加1, 不過在對象前 加__block修飾,則參考計數不變。

__weak typeof(self) weakSelf = self;
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved