iOS學習-Block

一抓督、是什么

Block本質(zhì)上也是一個OC對象裹粤,底層也是一個結(jié)構(gòu)體,內(nèi)部也有isa指針恬吕。
封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象儒喊。
底層結(jié)構(gòu)如 圖所示:


二、block的基本使用

2.1 block的形式

  • 無參無返回值
//定義一個block
void (^myblock1)()=^(){

  NSLog(@"myblock1");

};
//使用block
myblock1();
  • 無參有返回值
int (^myblock2)()=^(){

return 8;
  NSLog(@"myblock2");

};

myblock2();
  • 有參無返回值
void (^myblock3)(int,int)=^(int a, int b){
  NSLog(@"%d",a+b);
};

myblock3(3,7);

//也可以先定義變量币呵,再賦值
myblock3 =^(int x,int y){
      NSLog(@"%d",x-y);
};

myblock3(8,7);
  • 有參有返回值
int (^myblock4)(int,int)=^(int a, int b){

  NSLog(@"%d",a+b);

  return a + b;
};
int sum = myblock4(3,4);
NSLog(@"%d",sum);

三怀愧、block的typedef

利用typedef定義block類型(和指向函數(shù)的指針很像)

  • 格式
    typedef 返回值類型 (^新別名)(參數(shù)類型列表);
    typedef int (^myblock)(int , int);

以后就可以利用這種類型來定義block變量了余赢;

typedef void (^Block)();
Block b1;
//Block類型的變量
b1 = ^{
     NSLog(@"myblock");
};

//有參數(shù)有返回值
typedef int (^newType)(int , int);
newType nt1 = ^(int a, int b){
   
retutn a +b;
};
int s = nt1 (12 , 23);
NSLog(@"%d"芯义,s);

//聯(lián)系定義多個newType的變量
newType n1,n2,n3
n1 = ^(int x, int y){
   
retutn x*y;
};

四、block對變量的捕獲

image.png
   //auto 可以不寫妻柒,默認的值扛拨,離開作用域會自動銷毀
//    auto int age = 10;
    
    int a = 10;//傳的變量,block內(nèi)部存儲的是個變量

    static int b = 10;//傳的地址值举塔,block內(nèi)部存儲的是個地址值
    
    void (^block)(void) = ^{
        
        NSLog(@"捕獲的值局部變量a=%d,靜態(tài)變量b=%d绑警,全局變量=%d",a,b,c);
        
    };
    
    a = 20;
    b = 30;
    c = 40;
    
    block();
    
    
    //1、為什么局部變量需要捕獲央渣,全局變量不需要捕獲计盒?
//   因為在底層代碼實現(xiàn)里面: 局部變量需要跨函數(shù)訪問,所以需要捕獲芽丹,而全局變量在哪個函數(shù)都可以直接訪北启,不需要捕獲。
    
    //1拔第、為什么2者有這的差異咕村?
    //auto類型代碼執(zhí)行完就要銷毀,所以是值傳遞蚊俺,不然再去訪問就會出現(xiàn)問題懈涛,因為局部變量已釋放。
    //  而靜態(tài)變量一直在內(nèi)存中泳猬, 不會銷毀批钠,所以是指針傳遞
    
    void (^block1)(void) = ^{
        
        NSLog(@"self=%p",self);
    };
    
    block1();
//  self也會被捕獲 
2021-09-18 15:16:46.086277+0800 OCStudy[90772:4443186] 捕獲的值a=10,b=30

代碼內(nèi)部實現(xiàn):


image.png

五宇植、block的類型

block 有3種類型,可以通過調(diào)用class方法或者isa指針查看具體類型价匠,最終都是繼承自NSBlock類型

image.png
  • MRC環(huán)境下的Block類型:
image.png

打開MRC


image.png
  void (^block)(void) = ^{
            
            NSLog(@"hello");
        };
        NSLog(@"block的類型=%@",[block class]);
        NSLog(@"block的父類=%@",[[block class] superclass]);
        NSLog(@"block的父類的父類=%@",[[[block class] superclass] superclass]);
block的類型=__NSGlobalBlock__
block的父類=NSBlock
block的父類的父類=NSObject
void (^block)(void) = ^{//__NSGlobalBlock__

            NSLog(@"hello");
        };
        
        NSLog(@"block的類型=%@",[block class]);

         
        int a = 10;
        void (^block1)(void) = ^{//__NSStackBlock__

            NSLog(@"age=%d",a);
        };
        NSLog(@"block1的類型=%@",[block1 class]);
        
        NSLog(@"block2的類型=%@",[[block1 copy] class]);//__NSMallocBlock__

        NSLog(@"block3的類型=%@",[[block copy] class]);//__NSGlobalBlock__
2021-09-20 15:50:48.324228+0800 dddd[50990:5734435] block的類型=__NSGlobalBlock__
2021-09-20 15:50:48.325460+0800 dddd[50990:5734435] block1的類型=__NSStackBlock__
2021-09-20 15:50:48.325632+0800 dddd[50990:5734435] block2的類型=__NSMallocBlock__
2021-09-20 15:50:48.325756+0800 dddd[50990:5734435] block3的類型=__NSGlobalBlock__
image.png

所以在MRC時代当纱,block用Copy修飾。
將棧的數(shù)據(jù)拷貝到堆上踩窖,防止局部變量的數(shù)據(jù)釋放掉以后坡氯,block還去訪問無數(shù)據(jù)的問題。

  • ARC環(huán)境下的Block類型:

ARC環(huán)境下,編譯器會根據(jù)情況自動將棧上的block復制到堆上洋腮,有以下情況:

  • block作為函數(shù)返回值時
  • 將block賦值給__strong指針時
  • cocoa API中箫柳,將block作為參數(shù)的,比如GCD的函數(shù)
void (^block)(void) = ^{//__NSGlobalBlock__
        
        NSLog(@"hello");
    };

 int a = 10;
    void (^block1)(void) = ^{//__NSMallocBlock__
        
        NSLog(@"age=%d",a);
    };
    NSLog(@"block1的類型=%@",[block1 class]);
2021-09-20 16:15:44.560817+0800 OCStudy[51711:5753274] block的類型=__NSGlobalBlock__
2021-09-20 16:15:44.561154+0800 OCStudy[51711:5753274] block1的類型=__NSMallocBlock__

六啥供、 __block修改局部auto變量

__block不能修飾局部變量悯恍、靜態(tài)變量
編譯器會將__block變量包裝成一個對象

__block int age = 10;//會將age包裝成一個對象
    NSLog(@"age的值=%d,地址=%p",age,&age);
    
    NSMutableArray *arr = [NSMutableArray array];
    
    void (^block)(void) = ^{
        
        age = 20;
        NSLog(@"age的值=%d,地址=%p",age,&age);
//        arr = nil;//這是修改值
        [arr addObject:@"123"];//不是 修改指針,是使用指針
        [arr addObject:@"456"];

    };
    
    block();
    
    NSLog(@"age的值=%d,地址=%p",age,&age);
    NSLog(@"arr的值=%@",arr);
image.png

image.png
image.png

七伙狐、 循環(huán)引用

typedef void (^TestBlock)(void)
@interface Person 
@property (nonatomic , strong) TestBlock testblock;
@end
-(void)dealloc{
    
    NSLog(@"person-delloc,死了");
}
  • 不使用weakself的情況涮毫。

    Person *person = [[Person alloc] init];
    person.age = 100;
    person.testblock = ^{

       NSLog(@"age2 = %d",person.age);
    };
    person.testblock();
    NSLog(@"end");
image.png

person和block相互強引用,造成循環(huán)引用贷屎,所以person對象無法dealloc罢防。

  • 使用weakself
    Person *person = [[Person alloc] init];
    person.age = 100;
    
    __weak typeof(person) weakPerson = person;
    person.testblock = ^{

       NSLog(@"age1 = %d",weakPerson.age);
    };
    person.testblock();
    NSLog(@"end");
image.png

由于一個使用__weak修飾,破除了相互強引用唉侄,所以peron可以delloc

  • 來看下面一段代碼 咒吐,在block內(nèi)部,有一個2秒后需要執(zhí)行的代碼属划,也需要用到age的值
Person *person = [[Person alloc] init];
    person.age = 100;

    __weak typeof(person) weakPerson = person;
    person.testblock = ^{

       NSLog(@"age1 = %d",weakPerson.age);
       
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       
                   NSLog(@"age2 = %d",weakPerson.age);
        });
    };
    person.testblock();
    NSLog(@"end");
image.png

我們發(fā)現(xiàn)后面再去使用age的值時恬叹,person已經(jīng)死掉了,這個時候可以使用使用__strong

Person *person = [[Person alloc] init];
    person.age = 100;

    __weak typeof(person) weakPerson = person;
    person.testblock = ^{

       NSLog(@"age1 = %d",weakPerson.age);
        
        __strong __typeof(weakPerson)strongPerson = weakPerson;
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    
                   NSLog(@"age2 = %d",strongPerson.age);
        });
    };
    person.testblock();
    NSLog(@"end");
image.png

在 Block 內(nèi)如果需要訪問 self 的方法同眯、變量绽昼,建議使用 weakSelf。
如果在 Block 內(nèi)需要多次 訪問 self嗽测,則需要使用 strongSelf绪励。

八、關(guān)于block的問題

1唠粥、怎么解決block循環(huán)引用的問題?
__weak __typeof__(self) weakSelf = self; 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
 __strong __typeof(self) strongSelf = weakSelf;  
 [strongSelf doSomething];  
}); 
2停做、什么時候在 block里面用self晤愧,不需要使用weakself?

block和其他的對象之間不會相互強引用

比如UIView的動畫代碼,我們在使用UIView animateWithDuration:方法做動畫的時候蛉腌,并不需要使用weakself官份,因為引用持有關(guān)系是:
UIView 的某個負責動畫的對象持有block只厘,block 持有了self因為 self 并不持有 block,所以就沒有循環(huán)引用產(chǎn)生舅巷,因為就不需要使用 weak self 了羔味。

[UIView animateWithDuration:0.2 animations:^{
    self.alpha = 1;
}];
3、為什么 block 里面還需要寫一個 strong self钠右,如果不寫會怎么樣赋元?

在 block 中先寫一個 strong self,其實是為了避免在 block 的執(zhí)行過程中飒房,突然出現(xiàn) self 被釋放的尷尬情況搁凸。通常情況下,如果不這么做的話狠毯,還是很容易出現(xiàn)一些奇怪的邏輯护糖,甚至閃退。

我們以 AFNetworking 中 AFNetworkReachabilityManager.m 的一段代碼舉例:

__weak__typeof(self)weakSelf =self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {    
__strong__typeof(weakSelf)strongSelf = weakSelf;    
strongSelf.networkReachabilityStatus= status;

if(strongSelf.networkReachabilityStatusBlock) {  
      strongSelf.networkReachabilityStatusBlock(status);    
}};

如果沒有 strongSelf 的那行代碼嚼松,那么后面的每一行代碼執(zhí)行時嫡良,self 都可能被釋放掉了,這樣很可能造成邏輯異常献酗。

4寝受、block 里 strong self 后,block 不是也會持有 self 嗎凌摄?而 self 又持有 block 羡蛾,那不是又循環(huán)引用了?
__weak __typeof(self)weakSelf = self;    //1

[self.context performBlock:^{      
    [weakSelf doSomething];          //2
     __strong __typeof(weakSelf)strongSelf = weakSelf;  //3
    [strongSelf doAnotherSomething];        
}];

1.使用__weak __typeof是在編譯的時候,另外創(chuàng)建一個局部變量weak對象來操作self锨亏,引用計數(shù)不變痴怨。block 會將這個局部變量捕獲為自己的屬性,訪問這個屬性器予,從而達到訪問 self 的效果浪藻,因為他們的內(nèi)存地址都是一樣的
2.因為weakSelf和self是兩個變量,doSomething有可能就直接對self自身引用計數(shù)減到0了,所以在[weakSelf doSomething]的時候,你很難控制這里self是否就會被釋放了.weakSelf只能看著
3.__strong __typeof在編譯的時候,實際是對weakSelf的強引用.指針連帶關(guān)系self的引用計數(shù)會增加.但是你這個是在block里面,生命周期也只在當前block的作用域.所以,當這個block結(jié)束, strongSelf隨之也就被釋放了.不會影響block外部的self的生命周期.

5、關(guān)于Masonry里面的Block,為什么不需要使用乾翔?

關(guān)于Masonry里面的Block:函數(shù)參數(shù)里面的Block是局部的block(棧上)爱葵,block內(nèi)部引用self不會造成循環(huán)引用;是否會循環(huán)引用只看函數(shù)內(nèi)部是否copy了這個block(比如把它付給全局的Block)

6反浓、block的原理是怎樣的萌丈?本質(zhì)是什么?

Block本質(zhì)上也是一個OC對象雷则,底層也是一個結(jié)構(gòu)體辆雾,內(nèi)部也有isa指針。
封裝了函數(shù)調(diào)用以及函數(shù)調(diào)用環(huán)境的OC對象

7月劈、__block的作用是什么度迂?有什么使用注意點藤乙?

_block可以用于解決block內(nèi)部無法修改auto變量值的問題
__block不能修飾全局變量、靜態(tài)變量(static)
編譯器會將__block變量包裝成一個對象
注意:循環(huán)引用問題

8惭墓、block的屬性修飾詞為什么是copy坛梁?

在MRC環(huán)境下,block用Copy修飾腊凶。將棧的數(shù)據(jù)拷貝到堆上划咐,防止局部變量的數(shù)據(jù)釋放掉以后,block還去訪問無數(shù)據(jù)的問題吭狡。
在ARC環(huán)境下尖殃,使用strong修飾也可以,編譯器會根據(jù)情況自動將棧上的block復制到堆上

9、block在修改NSMutableArray划煮,需不需要添加__block送丰?

不需要

NSMutableArray *arr = [NSMutableArray array];
  
  void (^block)(void) = ^{

      arr = nil;   這是修改值
      [arr addObject:@"123"];  不是 修改指針,是使用指針進行操作
      [arr addObject:@"456"];

  };
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末弛秋,一起剝皮案震驚了整個濱河市器躏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蟹略,老刑警劉巖登失,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挖炬,居然都是意外死亡揽浙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門意敛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來馅巷,“玉大人,你說我怎么就攤上這事草姻〉鲡” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵撩独,是天一觀的道長敞曹。 經(jīng)常有香客問我,道長综膀,這世上最難降的妖魔是什么澳迫? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮剧劝,結(jié)果婚禮上纲刀,老公的妹妹穿的比我還像新娘。我一直安慰自己担平,他們只是感情好示绊,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著暂论,像睡著了一般面褐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上取胎,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天展哭,我揣著相機與錄音,去河邊找鬼闻蛀。 笑死匪傍,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的觉痛。 我是一名探鬼主播役衡,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼薪棒!你這毒婦竟也來了手蝎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤俐芯,失蹤者是張志新(化名)和其女友劉穎棵介,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吧史,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡邮辽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贸营。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吨述。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖莽使,靈堂內(nèi)的尸體忽然破棺而出锐极,到底是詐尸還是另有隱情,我是刑警寧澤芳肌,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布灵再,位于F島的核電站,受9級特大地震影響亿笤,放射性物質(zhì)發(fā)生泄漏翎迁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一净薛、第九天 我趴在偏房一處隱蔽的房頂上張望汪榔。 院中可真熱鬧,春花似錦肃拜、人聲如沸痴腌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽士聪。三九已至锦援,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剥悟,已是汗流浹背灵寺。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留区岗,地道東北人略板。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像慈缔,于是被迫代替她去往敵國和親叮称。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

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