iOS巔峯之block(由淺到深)詳解

導語

不會使用block的iOS程序員,不是一個合格的程序員  
學會了block,你再也不想用繁瑣的代理 
block沒有你想象中的那麼難,不要害怕,不要畏懼,勇敢嘗試 

什麼是block

block其實就是一個代碼塊,把你想要執行的代碼封裝在這個代碼塊裏,等到需要的時候再去調用。那block是OC對象嗎?答案是肯定的

來自官方文檔
筆者以英語3.9級的水平給大家翻譯下,“block是一個OC對象,這意味着它能被添加到集合,比如NSArray、NSDictionary”

block的定義

  1. block屬性或變量 
    格式:返回值類型(^block名稱)(參數列表) 
<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"> <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*定義屬性,block屬性可以用strong修飾,也可以用copy修飾
  有小夥伴留言說蘋果官方建議用copy,筆者查了下文檔,
  確實是這樣的,不過筆者未測試出copy與strong的區別,大家喜歡啥就用啥吧*/</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^myBlock)();<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//無參無返回值</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^myBlock1)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//帶參數</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>) <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *(^myBlock2)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//帶參數與返回值</span>
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//定義變量</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^myBlock)() = <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//無參無返回值</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^myBlock1)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *) = <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//帶參數</span>
 <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *(^myBlock2)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *) = <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//帶參數與返回值</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

2.block被當做方法的參數  
格式:(block類型)參數名稱

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"> - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)test:(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^)())testBlock<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//無慘無返回值</span>
 - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)test1:(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *))testBlock<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//帶參數</span>
 - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)test2:(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *(^)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *))testBlock<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//帶參數與返回值</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

3.使用typedef定義block

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">typedef</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^myBlock)(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//以後就可以使用myBlock定義無參無返回值的block</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">typedef</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^myBlock1)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//使用myBlock1定義參數類型爲NSString的block</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">typedef</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *(^myBlock2)(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//使用myBlock2定義參數類型爲NSString,返回值也爲NSString的block</span>
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//定義屬性</span>
 <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>) myBlock testBlock;
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//定義變量</span>
 myBlock testBlock = <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>;
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//當做參數</span>
 - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)test:(myBlock)testBlock;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

block的賦值

格式:block = ^返回值類型(參數列表){} 
1. 沒有參數沒有返回值

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">myBlock testBlock = ^<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(){
     <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"test"</span>);
 };
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//沒有返回值,void可以省略</span>
myBlock testBlock1 = ^(){
     <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"test1"</span>);
 };
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//沒有參數,小括號也可以省略</span>
 myBlock testBlock2 = ^{
     <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"test2"</span>);
 };</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

2.有參數沒有返回值

<code class="hljs axapta has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">myBlock1 testBlock = ^<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(NSString *<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">str</span>) {
      NSLog(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">str</span>);
}
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//省略void</span>
myBlock1 testBlock = ^(NSString *<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">str</span>) {
      NSLog(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">str</span>);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>

3.有參數有返回值

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">myBlock2 testBlock = ^<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *str) {
     <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(str)
     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"hi"</span>;
}
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//有返回值時也可以省略返回值類型</span>
 myBlock2 testBlock2 = ^(<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> *str) {
     <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(str)
     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"hi"</span>;
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

實戰

接下來,我們就結合一個實例程序,來看看block在實際開發中的簡單使用 
這裏寫圖片描述

本案例涉及到兩個控制器與一個Person類

  1. 聯繫人列表控制器:使用tableView展示聯繫人列表,稱爲A控制器
  2. 新建聯繫人控制器:創建新的聯繫人對象,稱爲B控制器
  3. Person:聯繫人,有兩個屬性,name與phoneNumber

任務需求:點擊A控制器右上角“新建”按鈕跳到B控制器,B控制器添加聯繫人後,點擊“保存”按鈕返回A控制器,並將新添加的聯繫人展示到列表中

問題來了,如何將B控制器中的數據傳遞給A控制器呢?

那還不簡單,A控制器直接把聯繫人數組傳遞給B控制器,B控制器新建聯繫人後添加到數組中,然後返回A控制器,在A控制器的viewWillAppear方法中刷新表格就OK了。

方法可行,但是不得不說,相當low,B控制器是用來添加聯繫人的,至於聯繫人數組什麼情況,無需關心,所以,不要把數組傳遞給B控制器

B控制器要做的僅僅只是,新建聯繫人,然後把聯繫人對象傳遞給A控制器,至於A控制器拿到聯繫人後會做什麼,那是A的事情,與B無關

看到這裏,很多人可能已經想到了代理,沒錯,代理也可以實現,但…是…,B控制器定義協議,聲明代理方法,A控制器設置代理,遵守協議,然後實現代理方法,B控制器在合適的地方調用代理方法,臥槽,好麻煩有木有,筆者都不想寫代碼了,還是回家種田去吧

好了不廢話了,進入正題

使用block傳遞數據

  1. 在B控制器的.h文件中定義一個沒有返回值,參數類型爲Person的block屬性
<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">@property</span> (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nonatomic</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span>) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>(^saveBlock)(Person *);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

2.在B控制器“保存”按鈕的點擊方法中調用block

<code class="hljs java has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"> - (IBAction)save:(id)sender {
     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//使用事先定義好的類方法創建Person對象</span>
  Person *person = [Person personWithName:_nameText.text phoneNumber:_phoneNumberText.text];
   <span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**調用block之前最好先判斷block是否爲空,不爲空才調用,否則程序崩潰*/</span>
   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//裝逼寫法</span>
 <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//!self.saveBlock? : self.saveBlock(person);</span>
   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//一般寫法</span>
   <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (self.saveBlock) {
       self.seveBlock(person);
   }
  [self.navigationController popViewControllerAnimated:YES];
 }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>

3.在A控制器中,給B控制器的block屬性進行賦值

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">   <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//“新建”按鈕點擊執行的方法</span>
   - (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>)newContact {
     AddContactViewController *addVC = [[AddContactViewController alloc] init];
     addVC<span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.saveBlock</span> = ^(Person *person){
          <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//這裏就可以拿到B控制器傳遞過來的person對象,添加到數組然後刷新表格</span>
      [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.contactList</span> addObject:person];
      [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.tableView</span> reloadData];
     };
     [<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">self</span><span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">.navigationController</span> pushViewController:addVC animated:<span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">YES</span>];
    }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>

三步就搞定,很簡單是不是,所以說,block並沒有你們想想的那麼複雜,自從筆者學會了block,就再也沒用過代理,除了系統的。

block常見雷區—循環引用

使用block有一個特別要注意的地方,循環引用,何爲循環引用?你引用我,我引用你,誰也不釋放誰,對象無法銷燬,佔用內存

我們來看一個循環引用的一個例子 
這裏寫圖片描述
注意看控制檯輸出,當點擊“取消”時,B控制器被銷燬,dealloc方法被調用

把註釋掉的代碼打開,再運行 
這裏寫圖片描述
點擊“取消”按鈕,B被移除,但是dealloc方法沒有調用,所以說,B控制器並沒有銷燬,why?

block對象賦值給了B控制器的屬性,因此B會對block有一個強引用,而block中又用到了self(B控制器對象),block會對使用到的外部變量進行捕獲,所以,block對B控制器也有一個強引用,最終造成循環引用,誰也無法釋放

循環引用解決方法

循環引用如何解決?很簡單,一行代碼搞定

這裏寫圖片描述
使用weakSelf(名稱隨便取的)替代self,block將不再對self進行強引用 
圖中__weak也可使用__unsafe_unretained,區別就是__weak修飾的指針,當對象銷燬後,指針會被自動置爲nil,而__unsafe_unretained修飾的指針,當對象銷燬後會變成野指針,爲了安全,推薦使用__weak

block的分類

block可分爲三種

  • NSStackBlock:棧block
  • NSMallocBlock:堆block
  • NSGlobalBlock:全局block
  1. 棧block 
    特點:生命週期由系統控制,函數返回即銷燬 
    用到局部變量、成員屬性變量,且沒有強指針引用的block都是棧block

a.用到局部變量(圖1),i爲局部變量,block直接在NSLog中打印,沒有被指針引用 
這裏寫圖片描述

b.用到成員屬性變量(圖2),name爲成員屬性 
這裏寫圖片描述

2.堆block 
特點:沒有強指針引用即銷燬,生命週期由程序員手動管理 
棧block如果有強指針引用或copy修飾的成員屬性引用就會被拷貝到堆中,變成堆block

a.強指針引用(圖3),block被testBlock引用,testBlock就是一個block類型的強指針(ARC環境下默認就是強指針) 
這裏寫圖片描述

b.copy修飾的成員屬性引用(圖4) 
這裏寫圖片描述

3.全局block 
特點:命長,有多長?很長很長,人在塔在(應用程序在它就在) 
沒有用到外界變量,或者只用到全局變量、靜態(static)變量的block就是全局block

對於全局block,有沒有指針引用都不影響,block類型的成員屬性無論是用assign、weak、strong還是copy修飾都無所謂,不過開發中很少用到全局block,所以不要用weak或assign

a.沒有用到外界變量(圖5),下圖中block沒有用到外界變量,所以就算用weak修飾也是全局block(舉個例子而已,開發中不要用weak,用了也別說是筆者教的) 
這裏寫圖片描述

b.只用到全局變量、靜態(static)變量(圖6),str爲全局變量,str1爲靜態變量,只用到其中一個也是全局block 
這裏寫圖片描述

分類總結 
1.沒有用到外界變量或只用到全局變量、靜態變量的block爲全局block,生命週期從創建到應用程序結束 
2.用到局部變量、成員屬性變量的block爲棧block,生命週期系統控制,函數返回即銷燬 
3.有強指針引用或copy修飾的成員屬性引用的block會被複制一份到堆中成爲堆block,沒有強指針引用即銷燬,生命週期由程序員控制

block對外界變量的捕獲

a.基本數據類型—局部變量 
block會拷貝該變量的值當做常量使用,外界修改變量的值不會影響block內部,且block內部不能對其修改

block內部修改外界變量i的值直接報錯,如果想要修改,可以在int i = 0前面加上關鍵字__block,此時i等效於全局變量或靜態變量 
這裏寫圖片描述

外界變量i從0變成了1,block內部打印依然是0 
這裏寫圖片描述

b.基本數據類型—靜態變量、全局變量、成員屬性變量 
block直接訪問變量地址,在block內部可以修改變量的值,並且外部變量被修改後,block內部也會跟着變

圖中_k爲成員屬性變量,初始值i = 10,j = 20,k = 0,block內部只對i、j、k進行一次自增操作,打印結果卻是i = 12,j = 22,k = 2,所以外部的自增操作也影響了內部,即訪問的是同一個內存地址 
這裏寫圖片描述

c.指針類型—局部變量 
block會複製一份指針並強引用指針所指對象,且內部不能修改指針的指向

圖中被註釋掉的代碼試圖修改指針指向,所以會報錯(如果想要修改,在前面加上__block),但是可以修改所指對象的值,如str從“abc”變成了“abcdef” 
這裏寫圖片描述

d.指針類型—全局變量、靜態變量、成員變量屬性 
block不會複製指針,但是會強引用該對象,內部可修改指針指向,block會強引用成員屬性變量所屬的對象,這也是爲什麼block屬性內部用到self.xxx會引起循環引用的原因

圖中str2爲成員屬性,由於NSString是不可變的,所以從打印結果可以看出,在block內部修改了外界指針變量的引用,指向了新的字符串 
這裏寫圖片描述

轉自:http://ios.jobbole.com/84127/

發佈了64 篇原創文章 · 獲贊 10 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章