日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS Block 知识点拾遗

發布時間:2023/12/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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內部也可以修改外部變量的值



    __block int a = 0;NSLog(@"定義前:%p", &a); //棧區void (^foo)(void) = ^{a = 1;NSLog(@"block內部:%p", &a); //堆區};NSLog(@"定義后:%p", &a); //堆區foo(); 復制代碼

    打印輸出:

    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 知识点拾遗的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。