iOS Block 知识点拾遗
生活随笔
收集整理的這篇文章主要介紹了
iOS Block 知识点拾遗
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Block為什么要用copy
- block在創建的時候默認分配的內存是在棧上,而不是在堆上。這樣的話其本身的作用域是屬于創建時候的作用域,一旦在創建的作用域之外調用就會導致程序的崩潰。所以使用了copy將其拷貝到堆內存上。
- block創建在棧上,而block的代碼中可能會用到本地的一些變量,在棧上可能隨時會被系統釋放掉,只有將其拷貝到堆上,才能一直保持Block的存在,并用這些外部變量
Block為什么不用retain
- retain只是增加了一次引用計數,block的內存還是在棧上,并沒有存在堆上,存在棧上的block可能隨時被系統回收
為什么進入block中的對象引用計數需要自動加1?
- Block執行的是回調,因此block并不知道其中的對象obj創建后會在什么時候被釋放,為了不在block使用obj之前,對象已經被釋放,block就retain了obj一次
block和函數的關系
- Block的使用很像函數指針,不過與函數最大的不同是Block可以訪問函數以外、詞法作用域以內的外部變量的值。換句話說,Block不僅 實現函數的功能,還能攜帶函數的執行環境。
- 函數指針 void(*fun)(int)
- block void(^fun)(int)
block本質(對于block的理解)請查閱Block實現原理
- block實際上是: 指向結構體的指針
- 編譯器會將block的內部代碼生成對應的函數
對于基本數據類型,進入到block中會被當做常量處理。對象 retain 會一次
//如果需要在block中對num進行修改,需要加上關鍵字__block//(我們也可以用static關鍵字進行修飾)int num1 = 10;void(^block1)() = ^{NSLog(@"num1 is %d",num1);};num1 = 20;block1(); //輸出10//改進:使用block,使進入到block塊中的變量不被當做常量來使用__block int num2 = 10;void(^block2)() = ^{NSLog(@"num2 is %d",num2);};num2 = 20;block2(); //輸出20復制代碼Block中self的循環引用
- block默認創建在棧上,所以對要對其進行執行copy操作,將其拷貝到堆區,便于更好的操作對象。但是執行了copy操作之后,block中使用self,此對象會被retain一次(注意:block在堆區上時才會起到retain作用),會造成循環引用。
- 解決方法:
- 在MRC下,使用__block修飾
- 在ARC下,使用__unsafe_unretained\weak修飾
Block 在內存中的分類
- 全局block --> GlobalBlock <==> 相當于全局變量, 系統會自動釋放
- 棧block --> StackBlock <==> 相當于局部變量, 系統會自動釋放
- 堆block --> MallocBlock <==> (需要手動釋放內存)
block類型區分方法
- 如果block實現中沒有訪問任何"外部"變量(包括局部和全局), 該block為GlobalBlock
- 如果block實現中訪問了任何"外部"變量(包括局部和全局), 該block為StackBlock
- 對StackBlock進行拷貝(copy/Block_copy), 該block為MallocBlock
block內如何修改block外 的變量?
__block int a = 0; void (^foo)(void) = ^{ a = 1; };foo(); //這里,a的值被修改為1復制代碼默認情況下,block內訪問的變量是 copy 該變量到堆后的變量,而非變量本身。
所以:讀寫操作對原變量不生效
我們知道:Block不允許修改外部變量的值,這里所說的外部變量的值,指的是棧中指針的內存地址。__block 所起到的作用就是只要觀察到該變量被 block 所持有,就將“外部變量”在棧中的內存地址放到了堆中。進而在block內部也可以修改外部變量的值
打印輸出:
2016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定義前:0x16fda86f82016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] 定義后:0x155b22fc82016-05-17 02:03:33.559 LeanCloudChatKit-iOS[1505:713679] block內部: 0x155b22fc8 復制代碼block個人理解
NSMutableString *kName = [NSMutableString stringWithString:@"kael"]; __block NSMutableString *myname = kName; NSLog(@"block 前%p",&myname);void (^foo)(void) = ^(){NSLog(@"block 中1:%p",&myname);myname = [NSMutableString stringWithString:@"kaelinda"];NSLog(@"block 中2:%p",&myname);};foo();NSLog(@"block 后%p",&myname);/*** __block 修飾之前* 外部變量的內存地址是存到了棧* block 是在堆內,不清楚棧內的變量是否已經被釋放,所以連引用的準確性都是問題,更談不上是修改了* 所以,block 不允許修改棧中的變量(或者棧中指針的內存地址)*//*** __block 修飾之后* 初始化的時候,內存地址在棧內(因為并不確定block內是否會用到),* 當檢測到block內引用到了該對象,不管block有沒有被調用,都會 從棧內 copy 內存地址到 堆內,* 再操作的時候 操作的是堆內的內存地址*/復制代碼轉載于:https://juejin.im/post/5bbdb367e51d450e49682f92
總結
以上是生活随笔為你收集整理的iOS Block 知识点拾遗的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET CORE MVC 2.0
- 下一篇: 程序员都是吃青春饭的?32岁程序员面试直