为什么会晕车的原理,为什么

原电池原理为什么能加快反应速率
原电池的反应速率快是因为氧化还原反应是通过离子的得失电子反应的,由于电流的作用是电子的流向产生定向性而且还使电子的运动速度加快,由于以上两种原因使一个原子的电子更快的想另一中原子的方向偏移从而使整个化学反应的速度加快.
}

Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这个新功能“Blocks”。从那开始,Block就出现在iOS和Mac系统各个API中,并被大家广泛使用。一句话来形容Blocks,带有自动变量(局部变量)的匿名函数。

Block在OC中的实现如下:

以上介绍是Block的简要实现,接下来我们来仔细研究一下Block的捕获外部变量的特性以及__block的实现原理。

为了研究编译器的实现原理,我们需要使用 clang 命令。clang 命令可以将 Objetive-C 的源码改写成 C / C++ 语言的,借此可以研究 block 中各个特性的源码实现方式。该命令是

  • 1.Block捕获外部变量实质

一.Block捕获外部变量实质

拿起我们的Block一起来捕捉外部变量吧。

说到外部变量,我们要先说一下C语言中变量有哪几种。一般可以分为一下5种:

研究Block的捕获外部变量就要除去函数参数这一项,下面一一根据这4种变量类型的捕获情况进行分析。

写出Block测试代码。

这里很快就出现了一个错误,提示说自动变量没有加__block,由于__block有点复杂,我们先实验静态变量,静态全局变量,全局变量这3类。测试代码如下:

这里就有2点需要弄清楚了
1.为什么在Block里面不加__bolck不允许更改变量?
2.为什么自动变量的值没有增加,而其他几个变量的值是增加的?自动变量是什么状态下被block捕获进去的?

为了弄清楚这2点,我们用clang转换一下源码出来分析分析。

首先全局变量global_i和静态全局变量static_global_j的值增加,以及它们被Block捕获进去,这一点很好理解,因为是全局的,作用域很广,所以Block捕获了它们进去之后,在Block里面进行++操作,Block结束之后,它们的值依旧可以得以保存下来。

接下来仔细看看自动变量和静态变量的问题。

 
这个构造函数中,自动变量和静态变量被捕获为成员变量追加到了构造函数中。

到此,__main_block_impl_0结构体就是这样把自动变量捕获进来的。也就是说,在执行Block语法的时候,Block语法表达式所使用的自动变量的值是被保存进了Block的结构体实例中,也就是Block自身中。
这里值得说明的一点是,如果Block外面还有很多自动变量,静态变量,等等,这些变量在Block里面并不会被使用到。那么这些变量并不会被Block捕获进来,也就是说并不会在构造函数里面传入它们的值。
Block捕获外部变量仅仅只捕获Block闭包里面会用到的值,其他用不到的值,它并不会去捕获。
再研究一下源码,我们注意到__main_block_func_0这个函数的实现
我们可以发现,系统自动给我们加上的注释,bound by copy,自动变量val虽然被捕获进来了,但是是用 __cself->val来访问的。Block仅仅捕获了val的值,并没有捕获val的内存地址。所以在__main_block_func_0这个函数中即使我们重写这个自动变量val的值,依旧没法去改变Block外面自动变量val的值。
OC可能是基于这一点,在编译的层面就防止开发者可能犯的错误,因为自动变量没法在Block中改变外部变量的值,所以编译过程中就报编译错误。错误就是最开始的那张截图。
小结一下:
到此为止,上面提出的第二个问题就解开答案了。自动变量是以值传递方式传递到Block的构造函数里面去的。Block只捕获Block中会用到的变量。由于只捕获了自动变量的值,并非内存地址,所以Block内部不能改变自动变量的值。Block捕获的外部变量可以改变值的是静态变量,静态全局变量,全局变量。上面例子也都证明过了。
剩下问题一我们还没有解决。
回到上面的例子上面来,4种变量里面只有静态变量,静态全局变量,全局变量这3种是可以在Block里面被改变值的。仔细观看源码,我们能看出这3个变量可以改变值的原因。
  1. 静态全局变量,全局变量由于作用域的原因,于是可以直接在Block里面被改变。他们也都存储在全局区。

  2. 静态变量传递给Block是内存地址值,所以能在Block里面直接改变值。

 
根据官方文档我们可以了解到,苹果要求我们在自动变量前加入 __block关键字(__block storage-class-specifier存储域类说明符),就可以在Block里面改变外部自动变量的值了。
总结一下在Block中改变变量值有2种方式,一是传递内存地址指针到Block中,二是改变存储区方式(__block)。
先来实验一下第一种方式,传递内存地址到Block中,改变变量的值。

看结果是成功改变了变量的值了,转换一下源码。
在__main_block_func_0里面可以看到传递的是指针。所以成功改变了变量的值。
至于源码里面的copy和dispose下一节会讲到。
改变外部变量值的第二种方式是加 __block这个放在第三章里面讨论,接下来我们先讨论一下Block的copy的问题,因为这个问题会关系到 __block存储域的问题。
 
 

先来说明一下3者的区别。
1.从捕获外部变量的角度上来看
 
  • 只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock。
    StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了。

  • 有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,没有强指针引用即销毁,生命周期由程序员控制

 



所以在ARC环境下,3种类型都可以捕获外部变量。
2.从持有对象的角度上来看:
 
 

 


 

 


 

 



由于_NSConcreteStackBlock所属的变量域一旦结束,那么该Block就会被销毁。在ARC环境下,编译器会自动的判断,把Block自动的从栈copy到堆。比如当Block作为函数返回值的时候,肯定会copy到堆上。





以上4种情况,系统都会默认调用copy方法把Block赋复制


但是当Block为函数参数的时候,就需要我们手动的copy一份到堆上了。这里除去系统的API我们不需要管,比如GCD等方法中本身带usingBlock的方法,其他我们自定义的方法传递Block为参数的时候都需要手动copy一份到堆上。


copy函数把Block从栈上拷贝到堆上,dispose函数是把堆上的函数在废弃的时候销毁掉。


上面是源码中2个常用的宏定义和4个常用的方法,一会我们就会看到这4个方法。





上面这一段是Block_release的一个实现,实现了怎么释放一个Block。对应有6个步骤。


上述2个方法的详细解析可以看这篇


回到上一章节中最后的例子,字符串的例子中来,转换源码之后,我们会发现多了一个copy和dispose方法。





相应的增加了2个方法。


 





 
我们继续研究一下__block实现原理。
 
先来看看普通变量的情况。
把上述代码用clang转换成源码。
从源码我们能发现,带有 __block的变量也被转化成了一个结构体__Block_byref_i_0,这个结构体有5个成员变量。第一个是isa指针,第二个是指向自身类型的__forwarding指针,第三个是一个标记flag,第四个是它的大小,第五个是变量值,名字和变量名同名。

 
源码中是这样初始化的。__forwarding指针初始化传递的是自己的地址。然而这里__forwarding指针真的永远指向自己么?我们来做一个实验。


 
我们把Block拷贝到了堆上,这个时候打印出来的2个i变量的地址就不同了。


地址不同就可以很明显的说明__forwarding指针并没有指向之前的自己了。那__forwarding指针现在指向到哪里了呢?


Block里面的__block的地址和Block的地址就相差1052。我们可以很大胆的猜想,__block现在也在堆上了。


出现这个不同的原因在于这里把Block拷贝到了堆上。


由第二章里面详细分析的,堆上的Block会持有对象。我们把Block通过copy到了堆上,堆上也会重新复制一份Block,并且该Block也会继续持有该__block。当Block释放的时候,__block没有被任何对象引用,也会被释放销毁。








这里还有一个需要注意的地方。还是从例子说起:


 
结果和之前copy的例子完全不同。


Block在捕获住__block变量之后,并不会复制到堆上,所以地址也一直都在栈上。这与ARC环境下的不一样。





感谢@酷酷的哀殿 指出错误,感谢@bestswifter 指点。上述说法有点不妥,详细见文章末尾更新。








至此,文章开头提出的问题一,也解答了。__block的实现原理也已经明了。

 

 



 
我们把上面的代码转换成源码研究一下:


首先需要说明的一点是对象在OC中,默认声明自带__strong所有权修饰符的,所以main开头我们声明的





在转换出来的源码中,我们也可以看到,Block捕获了__block,并且强引用了,因为在__Block_byref_block_obj_0结构体中,有一个变量是id block_obj,这个默认也是带__strong所有权修饰符的。


根据打印出来的结果来看,ARC环境下,Block捕获外部对象变量,是都会copy一份的,地址都不同。只不过带有__block修饰符的变量会被捕获到Block内部持有。


我们再来看看MRC环境下的情况,还是将上述代码的例子运行在MRC中。





 






在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象,所以才会产生循环引用的问题!

 
关于Block捕获外部变量有很多用途,用途也很广,只有弄清了捕获变量和持有的变量的概念以后,之后才能清楚的解决Block循环引用的问题。
再次回到文章开头,5种变量,自动变量,函数参数 ,静态变量,静态全局变量,全局变量,如果严格的来说,捕获是必须在Block结构体__main_block_impl_0里面有成员变量的话,Block能捕获的变量就只有带有自动变量和静态变量了。捕获进Block的对象会被Block持有。
对于非对象的变量来说,
自动变量的值,被copy进了Block,不带__block的自动变量只能在里面被访问,并不能改变值。

带__block的自动变量 和 静态变量 就是直接地址访问。所以在Block里面可以直接改变变量的值。

而剩下的静态全局变量,全局变量,函数参数,也是可以在直接在Block中改变变量值的,但是他们并没有变成Block结构体__main_block_impl_0的成员变量,因为他们的作用域大,所以可以直接更改他们的值。
值得注意的是,静态全局变量,全局变量,函数参数他们并不会被Block持有,也就是说不会增加retainCount值。

在MRC环境下,__block根本不会对指针所指向的对象执行copy操作,而只是把指针进行的复制。
而在ARC环境下,对于声明为__block的外部对象,在block内部会进行retain,以至于在block环境内能安全的引用外部对象。





}

本书运用彼得原理从不同的角度对现代企业中的各个环节上出现的问题进行分析和总结,并结合最典型的案例提出了解决的方法,希望对于现代企业管理者有较大的借鉴意义。

彼得原理:为什么事情总是出错

  ——不合理的制度是滋生错误的温床
  彼得处方NO.1:建立严谨完善的企业制度,杜绝消极的员工 002
  彼得处方NO.2:垄断控制导致不公平,不公平必然引发冲突 007
  彼得处方NO.3:走出管理思维误区,弥补管理制度缺陷 012
  彼得处方NO.4:主观的危机,好制度不是一个人可以制定的 016
  彼得处方NO.5:错误地把企业利益与员工利益对立起来 020
  ——会协调、懂经营,才会少犯错
  彼得处方NO.1:不懂得褒奖——低效企业管理者最常犯的错误 026
  彼得处方NO.2:分享利润,打造一支和谐一致的团队 031
  彼得处方NO.3:企业管理层在经营中最常犯的错
  ——只重视制度,而轻视执行 035
  彼得处方NO.4: 经营需要魄力,不需要“唆” 038
  ——缺乏积极性的环境是庸人们的天堂
  彼得处方NO.1:坐等机会的人永远不会有机会 044
  彼得处方NO.2:为什么世界上到处都是有才华的穷人 049
  彼得处方NO.3:摆正位置的人才会得到幸运女神的青睐 053
  彼得处方NO.4:找不到自己新的闪亮的“卖点” 058
  ——合理有效的管理是降低错误的最有效方法
  彼得处方NO.1:让平凡的员工做出不平凡的业绩 064
  彼得处方NO.2:墨守成规,引发重重失误 070
  彼得处方NO.3:不懂得适度放权——这样的管理方式永远培养
  不出少犯错误的员工 075
  彼得处方NO.4:不够透明犯的错——你的管理为什么总是失效 081
  彼得处方NO.5:不懂得目标管理——没有目标的管理都是一团糟 086
  ——人缘越好,错误越少
  彼得处方NO.1:诚信缺失——很多错误都源自于失信 090
  彼得处方NO.2:凡事斤斤计较,缺乏奉献精神 094
  彼得处方NO.3:如果你不遵守游戏规则,错误就来了 099
  彼得处方NO.4:如果你做错了,就赶紧承认 104
  彼得处方NO.5:猜疑本身就是一个错误,而且会带来恶劣的影响 109
  彼得处方NO.6:嫉妒是毒药——企业管理者不需要一颗嫉妒的心 113
  ——安排不好时间,错误就会找上门
  彼得处方NO.1:时间是最紧缺的资源 120
  彼得处方NO.2:记录你的时间的使用情况 125
  彼得处方NO.3:分析你的时间的使用情况 129
  彼得处方NO.4:重新分配时间 135
  彼得处方NO.5:将时间化零为整 139
  ——不胜任是管理中最大的弊端
  彼得处方NO.1:不胜任已成为普遍的社会现象 144
  彼得处方NO.2:从组织架构上杜绝不胜任 148
  彼得处方NO.3:从职位设计上避免不胜任 153
  彼得处方NO.4:在人才选拔的程序和标准上避免不胜任 156
  彼得处方NO.5:加大培训以减少不胜任 160
  ——正确的决策来自于正确的理念
  彼得处方NO.1:更短的劳动时间,更强的竞争力 166
  彼得处方NO.2:是否进步取决于你如何对待错误 172
  彼得处方NO.3:忠诚来自于情感投资 177
  彼得处方NO.4:答案永远在现场 184
  彼得处方NO.5:榜样的力量不是无穷的 188
  彼得处方NO.6:闭嘴,我不想听你的借口 193
  彼得处方NO.7:不给不胜任的员工任何的借口 196
  ——以最高效的方式去激励员工
  彼得处方NO.1:奖金不一定是绩效推动剂 202
  彼得处方NO.2:让员工自己选择奖励方式 205
  彼得处方NO.3:满足员工更高层次的需求 210
  彼得处方NO.4:如何激励知识型员工 217
  彼得处方NO.5:福利能够让员工更有安全感 221
  ——减少错误,走出层级组织的金字塔
  彼得处方NO.1:解开层级制度之谜的钥匙 226
  彼得处方NO.2:层级组织的利与弊 233
  彼得处方NO.3:如何走出不断膨胀的层级组织的金字塔 237
  彼得处方NO.4:如何克服不断膨胀的层级组织带来的问题 242


}

我要回帖

更多关于 为什么会晕车的原理 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信