<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="https://hql-yunyunyun.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hql-yunyunyun.github.io/" rel="alternate" type="text/html" /><updated>2019-04-27T10:20:35+00:00</updated><id>https://hql-yunyunyun.github.io/feed.xml</id><title type="html">HQL iOS</title><subtitle>iOS Developer from Somewhere</subtitle><entry><title type="html">通过PHPhotoLibrary获取UIImage内存不释放的bug</title><link href="https://hql-yunyunyun.github.io/%E9%80%9A%E8%BF%87PHPhotoLibrary%E8%8E%B7%E5%8F%96UIImage%E5%86%85%E5%AD%98%E4%B8%8D%E9%87%8A%E6%94%BE%E7%9A%84bug/" rel="alternate" type="text/html" title="通过PHPhotoLibrary获取UIImage内存不释放的bug" /><published>2019-04-22T00:00:00+00:00</published><updated>2019-04-22T00:00:00+00:00</updated><id>https://hql-yunyunyun.github.io/%E9%80%9A%E8%BF%87PHPhotoLibrary%E8%8E%B7%E5%8F%96UIImage%E5%86%85%E5%AD%98%E4%B8%8D%E9%87%8A%E6%94%BE%E7%9A%84bug</id><content type="html" xml:base="https://hql-yunyunyun.github.io/%E9%80%9A%E8%BF%87PHPhotoLibrary%E8%8E%B7%E5%8F%96UIImage%E5%86%85%E5%AD%98%E4%B8%8D%E9%87%8A%E6%94%BE%E7%9A%84bug/">&lt;h2 id=&quot;bug简述&quot;&gt;bug简述&lt;/h2&gt;
&lt;p&gt;现在有这么一个场景：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;需要从相册中获取数量不定的图片，然后需要将获取到的图片转换成视频，因为现在照片有可能需要在iCloud上下载，所以不能获取一张图片之后直接生成视频，而是需要将所有选中的图片获取之后并记录在数组当中，当所有图片获取完成再逐张图片生成视频，在所有操作都完成之后就将图片释放。&lt;/li&gt;
  &lt;li&gt;获取图片使用的方法是:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PHImageRequestID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;requestImageForAsset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PHAsset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;asset&lt;/span&gt; 
                              &lt;span class=&quot;nf&quot;&gt;targetSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CGSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;targetSize&lt;/span&gt; 
                             &lt;span class=&quot;nf&quot;&gt;contentMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PHImageContentMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;contentMode&lt;/span&gt; 
                                 &lt;span class=&quot;nf&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nullable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PHImageRequestOptions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt; 
                           &lt;span class=&quot;nf&quot;&gt;resultHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIImage&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__nullable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSDictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__nullable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;resultHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;流程： 选中图片(PHAsset) –&amp;gt; 循环选中图片数组(PHAsset)获取图片 –&amp;gt; 将获取到的图片都保存到一个数组中 –&amp;gt; 循环image数组生成视频 –&amp;gt; 释放image数组&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;bug：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;调用PHPhotoLibrary的方法获取图片时，图片将解码并将数据存在内存当中，这时根据我们的逻辑将对UIImage进行强引用，并在完成图片转视频的操作之后将UIImage给释放掉。&lt;/li&gt;
  &lt;li&gt;在我的设想中，这时这部分的内存就应该自动回收，但我发现在iPhone内存为2g以上的机器这部分的内存都不会自动回收(iPhone 6是会回收的)，并且当App进入到后台的时候这部分内存也会自动回收(这就表明我们的这部分代码是没有内存泄漏的)。&lt;/li&gt;
  &lt;li&gt;这应该是UIImage的缓存机制而引起的(太坑了)。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;bug解决方案&quot;&gt;bug解决方案&lt;/h2&gt;
&lt;p&gt;既然知道是因为UIImage缓存机制引起的bug，那么只要避免引起UIImage的缓存机制就OK了。如果我们不对UIImage强引用，Image是会正常释放掉内存的，所以这边在获取图片的方法改成了:&lt;/p&gt;
&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PHImageRequestID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;requestImageDataForAsset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PHAsset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;asset&lt;/span&gt; 
                                     &lt;span class=&quot;nf&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nullable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PHImageRequestOptions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;options&lt;/span&gt; 
                               &lt;span class=&quot;nf&quot;&gt;resultHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__nullable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imageData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSString&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__nullable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dataUTI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UIImageOrientation&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;orientation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSDictionary&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__nullable&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;resultHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;并在数组中对imageData强引用，这时内存是平稳的，并没有突增很大的内存(UIImage没有解码)，然后在图片生成视频前才将image创建出来，并且没有对image进行强引用(在创建UIImage的时候内存也没有增加，应该是没有对图片进行解码)。在视频生成之后，中间解码图片所产生的内存都随着类的释放而释放。
到目前为止，这个内存bug应该算是解决了，但为什么使用&lt;code class=&quot;highlighter-rouge&quot;&gt;[UIImage imageWithData:]&lt;/code&gt;就没有进行缓存，这跟我看到的一篇博文解释不一样 &lt;a href=&quot;https://blog.ibireme.com/2015/11/02/ios_image_tips/&quot;&gt;iOS 处理图片的一些小 Tip&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;通过数据创建 UIImage 时，UIImage 底层是调用 ImageIO 的 CGImageSourceCreateWithData() 方法。该方法有个参数叫 ShouldCache，在 64 位的设备上，这个参数是默认开启的。这个图片也是同样在第一次显示到屏幕时才会被解码，随后解码数据被缓存到 CGImage 内部。与 imageNamed 创建的图片不同，如果这个图片被释放掉，其内部的解码数据也会被立刻释放。&lt;/li&gt;
&lt;/ul&gt;</content><author><name></name></author><category term="bug记录" /><category term="基础" /><summary type="html">bug简述 现在有这么一个场景： 需要从相册中获取数量不定的图片，然后需要将获取到的图片转换成视频，因为现在照片有可能需要在iCloud上下载，所以不能获取一张图片之后直接生成视频，而是需要将所有选中的图片获取之后并记录在数组当中，当所有图片获取完成再逐张图片生成视频，在所有操作都完成之后就将图片释放。 获取图片使用的方法是:</summary></entry><entry><title type="html">哈希表</title><link href="https://hql-yunyunyun.github.io/%E5%93%88%E5%B8%8C%E8%A1%A8/" rel="alternate" type="text/html" title="哈希表" /><published>2019-04-20T00:00:00+00:00</published><updated>2019-04-20T00:00:00+00:00</updated><id>https://hql-yunyunyun.github.io/%E5%93%88%E5%B8%8C%E8%A1%A8</id><content type="html" xml:base="https://hql-yunyunyun.github.io/%E5%93%88%E5%B8%8C%E8%A1%A8/">&lt;h2 id=&quot;哈希表的概念&quot;&gt;哈希表的概念&lt;/h2&gt;

&lt;p&gt;我们现在来假设一下有一个电话本，需要存储名字/电话，那么一般来说会创建一个电话对象，电话对象有两个属性：名字和电话，并将这个电话对象存储到数组当中。当我们需要查找某一个电话的时候，就需要遍历数组，那么这个时间复杂度就为O(n)。如果每一次查找都需要遍历一遍数组，那么这个效率也太低了，如何来提高查找效率呢？&lt;/p&gt;

&lt;p&gt;这时候就出现了一个新的数据结构：Hash表。Hash表也称散列表，也有直接译作哈希表，Hash表是一种特殊的数据结构，它同数组、链表以及二叉排序树等相比较有很明显的区别，它能够快速定位到想要查找的记录，而不是与表中存在的记录的关键字进行比较来进行查找。这个源于Hash表设计的特殊性，它采用了函数映射的思想将记录的存储位置与记录的关键字关联起来，从而能够很快速地进行查找。&lt;/p&gt;

&lt;p&gt;回到上面的电话本如果使用哈希表来存储，以名字为key，电话为value，那么我们只要知道这个key，就可以知道它所对应的value，这个查找的时间复杂度就近似O(1)了。&lt;/p&gt;

&lt;h3 id=&quot;什么事哈希表&quot;&gt;什么事哈希表?&lt;/h3&gt;
&lt;p&gt;哈希表(Hash Table)又称为&lt;strong&gt;散列表&lt;/strong&gt;。哈希表是一种可以根据以key-value键值对形式存储数据的数据结构，可以通过关键字Key直接找到数据Value的存储位置，而不需要经过任何的遍历和比较。&lt;a href=&quot;https://baike.baidu.com/item/%E5%93%88%E5%B8%8C%E8%A1%A8/5981869?fr=aladdin&quot;&gt;哈希表_百度百科&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;哈希表中的四个概念&quot;&gt;哈希表中的四个概念&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;关键字(Key) — 哈希表通过一个信息(Key)来查找另外一个信息(Value)，将两个信息在表中形成映射关系，而关键字就是我们要提供的信息；&lt;/li&gt;
  &lt;li&gt;值(Value) — 存储的值；&lt;/li&gt;
  &lt;li&gt;哈希函数 — 哈希函数是构建哈希表的工具，是key和对应的value的存储位置的一个映射关系，通过把key代入到哈希函数中进行计算，就可以得到value在哈希表中的存储位置；&lt;/li&gt;
  &lt;li&gt;哈希地址 — 哈希地址记录的是我们所需要的数据在哈希表中的存储位置，哈希地址只是单纯的表示哈希表中的存储位置，不是实际的物理存储位置。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;关键字值哈希函数哈希地址哈希表之间的关系&quot;&gt;关键字、值、哈希函数、哈希地址、哈希表之间的关系？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;哈希表&lt;/strong&gt;是通过&lt;strong&gt;哈希函数&lt;/strong&gt;来构建的，假设哈希函数为&lt;code class=&quot;highlighter-rouge&quot;&gt;f()&lt;/code&gt;，而key就是x，既&lt;code class=&quot;highlighter-rouge&quot;&gt;f(x)&lt;/code&gt;。那么在经过哈希函数的计算之后可以求出一个值，这个值就是key所对应的&lt;strong&gt;值(value)&lt;/strong&gt;的存储地址，也就是&lt;strong&gt;哈希地址&lt;/strong&gt;。而记录这整个Key-Value信息的表就是&lt;strong&gt;哈希表&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;还是这个电话本，该电话本以拼音的首字母来区分数据，记录了人名和其电话号码，那么转换成哈希表，拼音首字母可以看成是哈希地址，人名为key，电话为value。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;哈希地址        key          value&lt;/li&gt;
  &lt;li&gt;​      A            爱丽丝   13000000000&lt;/li&gt;
  &lt;li&gt;​      B            包丽丝   13000000000&lt;/li&gt;
  &lt;li&gt;​      C            陈丽丝   13000000000&lt;/li&gt;
  &lt;li&gt;​      D            大丽丝   13000000000&lt;/li&gt;
  &lt;li&gt;​    ……&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们现在把这个电话本代入到我们上面的解析：f(爱丽丝) = A，f(包丽丝) = B等。就是我们知道了爱丽丝，并通过哈希函数计算出了其所代表的哈希地址为A，那么我们就知道了电话的存储地址进而知道爱丽丝的电话号码，那么现在我们就不要遍历整个表就可以直接得到我们先要的数据了。&lt;/p&gt;

&lt;h3 id=&quot;哈希冲突&quot;&gt;哈希冲突&lt;/h3&gt;
&lt;p&gt;因为哈希地址是通过哈希函数和key计算出来的，那么就有可能出现两个不一样的key计算出来的哈希地址是一样的，这种情况就是哈希冲突。回到电话本就是现在有一个爱丽丝，同时还有一个艾热，它们两者的拼音首字母都是A，这就意味着它们同时都存在电话本的A区，那么这就存在冲突了。&lt;/p&gt;

&lt;p&gt;有人说上面会冲突是因为我们的算法不够复杂而导致的，其实这是对的也是错的。如果当我们的地址无限大的同时可以保证我们的算法足够复杂不会导致重复，那么哈希冲突就不会出现的。但通常情况下，关键字的合集是大于地址合集的。&lt;/p&gt;

&lt;p&gt;地址合集是有限的，这就意味着哈希函数是一个压缩映象，那么哈希冲突是根本无法避免的。我们可以做的只是尽量避免冲突，所以我们可以将冲突的水平平均化，把关键字映射到地址集合中的每个一地址的概率是相等的，也就是我们后面会说到的均匀的哈希函数。&lt;/p&gt;

&lt;h3 id=&quot;哈希表的大小&quot;&gt;哈希表的大小&lt;/h3&gt;
&lt;p&gt;哈希表的大小也是构建哈希表的一个关键点，如果哈希表的空间远远大于最后实际存储的记录个数，则会造成很大的空间浪费，如果选小了，那么就会很容易造成哈希冲突。在实际的应用中，一般都是根据最终记录存储的个数和关键字分布的特点来确定哈希表的大小，但如果事先不知道存储个数的话，则需要动态维护哈希表的容量，此时可能需要重新计算哈希地址。&lt;/p&gt;

&lt;h3 id=&quot;哈希表的平均查找长度&quot;&gt;哈希表的平均查找长度&lt;/h3&gt;
&lt;p&gt;Hash表的平均查找长度包括查找成功时的平均查找长度和查找失败时的平均查找长度。&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;查找成功时的平均查找长度=表中每个元素查找成功时的比较次数之和/表中元素个数；
查找不成功时的平均查找长度相当于在表中查找元素不成功时的平均比较次数，可以理解为向表中插入某个元素，该元素在每个位置都有可能，然后计算出在每个位置能够插入时需要比较的次数，再除以表长即为查找不成功时的平均查找长度。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;哈希表的优缺点&quot;&gt;哈希表的优缺点&lt;/h3&gt;
&lt;p&gt;优点：可以提供快速插入和查找操作，无论有多少数据项，插入与查找只需接近常量的时间：O(1)时间级。而且编程很容易实现。&lt;/p&gt;

&lt;p&gt;缺点：它是基于数组的，数组一旦被创建，就难以拓展；某些哈希表的填充因子（填入的元素个数/哈希表长度)过大，性能会急剧下降。&lt;/p&gt;

&lt;h3 id=&quot;关于哈希表的性能&quot;&gt;关于哈希表的性能&lt;/h3&gt;
&lt;p&gt;由于哈希表高效的特性，查找或者插入操作在大多数的情况下都是达到O(1)，时间主要是花在计算哈希地址上，但也会有一种最坏的情况就是哈希地址全都映射到同一个地址上面，即每一个都有冲突，那么在解决冲突的情况下浪费大量的时间。&lt;/p&gt;

&lt;h2 id=&quot;常见的哈希函数构造方法&quot;&gt;常见的哈希函数构造方法&lt;/h2&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;怎样才算一个好的哈希函数&quot;&gt;怎样才算一个好的哈希函数？&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;均匀的哈希函数：
若对于关键字集合中任一关键字，经过哈希函数映射到地址集合的任何一个地址的概率都是相等的，则称此类函数为&lt;strong&gt;均匀的(Uniform)&lt;/strong&gt;哈希函数，从而减少哈希冲突。&lt;/li&gt;
  &lt;li&gt;如何判断一个哈希函数的优劣：
    &lt;ul&gt;
      &lt;li&gt;能否将关键字均匀映射到哈希空间上&lt;/li&gt;
      &lt;li&gt;有没有好的解决冲突的方法&lt;/li&gt;
      &lt;li&gt;计算哈希函数是否简单高效&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;常见的六个构建哈希函数的方法&quot;&gt;常见的六个构建哈希函数的方法&lt;/h3&gt;
&lt;p&gt;常见的哈希函数构建思路有六个:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;直接定值法&lt;/li&gt;
  &lt;li&gt;数字分析法&lt;/li&gt;
  &lt;li&gt;平方取中法&lt;/li&gt;
  &lt;li&gt;折叠法&lt;/li&gt;
  &lt;li&gt;除留余数法&lt;/li&gt;
  &lt;li&gt;随机数法&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;直接定值法&quot;&gt;直接定值法&lt;/h4&gt;
&lt;p&gt;直接定值法的哈希函数就是一个一次函数，去关键字和关键字的某个线性函数值为哈希地址：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;f (Key) = Key&lt;/li&gt;
  &lt;li&gt;f (Key) = a * Key + b&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;第一种形式的哈希地址就是key本身，而第二种形式中的a/b是常数，通过key和常数的计算来获取哈希地址。这类哈希函数叫做&lt;strong&gt;自身函数&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;直接定值法的哈希表非常简单，但相对来说哈希冲突出现的情况就会很多，不过在实际的使用中，比较少使用。&lt;/p&gt;

&lt;h4 id=&quot;数字分析法&quot;&gt;数字分析法&lt;/h4&gt;
&lt;p&gt;如果关键字由多位字符或者数字组成，就可以考虑抽取其中的2位或者多位来作为该关键字对应的哈希地址，在取法上尽量选择变化较多的位，避免发生冲突。
举个栗子：
		8 1 3 4 6 5 3 2
		8 1 3 7 2 3 4 7
  	  8 1 3 9 7 2 8 7
		8 1 4 7 9 7 5 2
		…
上面展示了4个关键字，每个关键字都是8位的十进制数字，经过分析我们会发现几个特征：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;第一位和第二位的值都是固定不变的&lt;/li&gt;
  &lt;li&gt;第三位的值为3/4&lt;/li&gt;
  &lt;li&gt;最后一位的值为2/7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;综上所述，只有中间4位数的值接近随机。所以为了避免冲突，我们从这4位中&lt;strong&gt;取任意两位&lt;/strong&gt;或者&lt;strong&gt;取其中两位和另外两位的和，再做舍去进位处理后得到的结果&lt;/strong&gt;作为哈希地址。&lt;/p&gt;

&lt;h4 id=&quot;平方取中法&quot;&gt;平方取中法&lt;/h4&gt;
&lt;p&gt;平方取中法是对关键字做平方操作，取中间几位作为哈希地址(此方法是比较常用的构造哈希函数的方法) 。
例如关键字序列为{421，423，436}，对各个关键字进行平方后的结果为{177241，178929，190096}，我们则可以取中间的两位{72，89，00}作为其哈希地址。&lt;/p&gt;

&lt;h4 id=&quot;折叠法&quot;&gt;折叠法&lt;/h4&gt;
&lt;p&gt;折叠法是将关键字分割成位数相同的几个部分(最后一部分的位数可以不同)，然后取这几部分的叠加和（舍去进位）作为哈希地址。关键字位数很多，且关键字每一位上数字分布大致均匀时，可以采用折叠法。&lt;strong&gt;折叠又可以分为两种：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;移位折叠 
&lt;strong&gt;(移动折叠是将分割后的每一部分的最低位对齐，然后相加)&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;间界折叠 
&lt;strong&gt;(间接叠加是从一端向另一端来回折叠，然后相加)&lt;/strong&gt;
举个栗子：
现有图书馆中某藏书的编号为0-442-20586-4，先对其分别采用移位折叠(a)和间界折叠(b)。如下图&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/HQL-yunyunyun/hql-yunyunyun.github.io/master/post_image/折叠法示意图.png&quot; alt=&quot;折叠法示意图&quot; title=&quot;折叠法示意图&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;(a) 移位折叠将图书编号作为关键字，分割为几个小的部分，然后以最低位对齐，然后相加，再舍去进位，得4位数作为哈希地址&lt;/li&gt;
  &lt;li&gt;(b) 间界折叠就类似于折纸的步骤。从一端向另一端折叠。同样是分割几个小的部分，然后最低位对齐，相加，舍去进位。与移位折叠不同的是，分割部分的数字序列顺序不同。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;除留余数法&quot;&gt;除留余数法&lt;/h4&gt;
&lt;p&gt;若已知整个哈希表的最大长度 m，可以取一个不大于 m 的数 p，然后对该关键字 key 做取余运算，即&lt;code class=&quot;highlighter-rouge&quot;&gt;f(key) = key % p&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;除留余数法也是&lt;strong&gt;最常用&lt;/strong&gt;，也最简单的哈希函数构造方法，它不仅可以直接去模，也可以先折叠，平方取中后再去模(结合几个方法)。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;除留余数法对p有很高的要求。若p选取的不好，很容易产生同义词。由以往经验可得，p一般取质数或不包含小于20的质因数的合数&lt;/p&gt;

&lt;h4 id=&quot;随机数法&quot;&gt;随机数法&lt;/h4&gt;
&lt;p&gt;即取关键字的一个随机函数值作为它的哈希地址，如&lt;code class=&quot;highlighter-rouge&quot;&gt;f(key) = random(key)&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;这里的随机函数其实是伪随机函数，真随机函数是即使每次给定的 key 相同，但是 H（key）都是不同；而伪随机函数正好相反，每个 key 都对应的是固定的 H（key）。&lt;/p&gt;

&lt;h3 id=&quot;常见哈希冲突的解决方法&quot;&gt;常见哈希冲突的解决方法&lt;/h3&gt;
&lt;p&gt;在哈希表的构建过程中，哈希冲突是无法避免的，那么必须采取适当的方法去处理这写冲突。这是常见的哈希冲突解决方法:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;开发定址法&lt;/li&gt;
  &lt;li&gt;再哈希法&lt;/li&gt;
  &lt;li&gt;链地址法(拉链法，位桶法)&lt;/li&gt;
  &lt;li&gt;公共溢出区&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;开发定址法&quot;&gt;开发定址法&lt;/h4&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;H&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MOD&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 这里可以看做是f(key) + d
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;f(key)是哈希函数，m是哈希表的长度，d是一个增量，d这里可以有三种取值:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;线性探测法(线性探测再散列): d = 1,2,3,4…,m-1;&lt;/li&gt;
  &lt;li&gt;二次探测法(二次探测再散列): d = 1^2,-(1^2),2^2,-(2^2)…,k^2,-(k^2) (k&amp;lt;=m-1)&lt;/li&gt;
  &lt;li&gt;伪随机数探测法(伪随机探测再散列): d = 伪随机序列
现有一个长度为11的哈希表，已填有关键字分别为17、60、29三条记录。其中采用的哈希函数为&lt;code class=&quot;highlighter-rouge&quot;&gt;f(key) = key MOD 11&lt;/code&gt;。现有第四个记录，关键字为38。根据原有的哈希算法，得出的哈希地址为5，跟关键字60的哈希地址一样，产生了冲突，根据增量d的取法不同，有一下三种情况:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/HQL-yunyunyun/hql-yunyunyun.github.io/master/post_image/开发定值法示意图.png&quot; alt=&quot;开发定值法示意图&quot; title=&quot;开发定值法示意图&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;线性探测法&lt;/strong&gt;:当发生了冲突，使用线性探测法&lt;code class=&quot;highlighter-rouge&quot;&gt;f(key) + d&lt;/code&gt;，所以首先得出的是&lt;code class=&quot;highlighter-rouge&quot;&gt;5 + 1 = 6&lt;/code&gt;得到下一个地址为6，但因为已经存在数据了再冲突，d就向下一位取，以此类推，最后得到空闲的哈希地址8，所以最后将数据填入哈希地址为8的空闲区域；&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;二次探测法&lt;/strong&gt;:首先d取值1^2，得到d = 1，得到哈希地址为6有冲突，取下一个数值-1^2，得到d = -1，哈希地址为4的空闲区域；&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;伪随机数探测法&lt;/strong&gt;:就是将d的取值交由伪随机数列来决定。
缺点:&lt;/li&gt;
  &lt;li&gt;线性探测法不利于查找，但可以保证只要哈希表没有被填满，就一定可以找到一个不发生冲突的位置；&lt;/li&gt;
  &lt;li&gt;二次探测法只有在哈希表长度m在&lt;code class=&quot;highlighter-rouge&quot;&gt;4j+3 // j为整数&lt;/code&gt;的素数时才能使用；&lt;/li&gt;
  &lt;li&gt;随机数探测法取决于伪随机序列。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;链地址法拉链法位桶法&quot;&gt;链地址法(拉链法，位桶法)&lt;/h4&gt;
&lt;p&gt;将产生冲突的关键字的数据存储在冲突哈希地址的一个线性链表中。&lt;/p&gt;

&lt;p&gt;例如有一组关键字为&lt;code class=&quot;highlighter-rouge&quot;&gt;{19,14,23,01,68,20,84,27,55,11,10,79}&lt;/code&gt; ，其哈希函数:&lt;code class=&quot;highlighter-rouge&quot;&gt;f (key)=key MOD 13&lt;/code&gt;，使用链地址法所构建的哈希表如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/HQL-yunyunyun/hql-yunyunyun.github.io/master/post_image/链地址法示意图.png&quot; alt=&quot;链地址法示意图&quot; title=&quot;链地址法示意图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;01,14,27,79 Mod 13&lt;/code&gt; 的都得&lt;code class=&quot;highlighter-rouge&quot;&gt;1&lt;/code&gt;，所以将它们对应的数据都存储在哈希地址为1的一个线性链表中。&lt;/p&gt;

&lt;h4 id=&quot;再哈希法&quot;&gt;再哈希法&lt;/h4&gt;
&lt;p&gt;再哈希法就是如果发生了冲突，则使用另外一个哈希函数来计算该关键字的地址，直到不发生冲突。此方法会增加计算时间。&lt;/p&gt;

&lt;h4 id=&quot;公共溢出区&quot;&gt;公共溢出区&lt;/h4&gt;
&lt;p&gt;建立一个公共溢出区，将产生冲突的数据都放到公共溢出区中。即：建立两张表，一张为基本表，另外一张为溢出表，基本表存放的是没有发生冲突的数据，溢出表存放的是发生冲突的数据，不管关键字通过哈希函数得到的哈希地址为什么，只要发生了冲突，都存放到溢出表中。&lt;/p&gt;

&lt;h2 id=&quot;哈希表的一些实现&quot;&gt;哈希表的一些实现&lt;/h2&gt;
&lt;hr /&gt;
&lt;p&gt;这里使用c++来创建一个哈希表:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/*
 哈希表的实现
    这里解决哈希冲突使用的是拉链法(同一哈希地址的value都放进一个链表中)
 */&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;#define HASHTABLESIZE 10 // 哈希表的大小
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;HashTable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HASHTABLESIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 构造方法
&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 析构函数
&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 哈希函数
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 查找节点
&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 添加
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 获取value
&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 显示所有的节点
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;构造函数:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 构造函数
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HASHTABLESIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 初始化节点
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 析构函数
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::~&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HASHTABLESIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;哈希函数，这里采用的是time33算法:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 哈希算法
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;33&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HASHTABLESIZE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;查找节点的方法:&lt;/p&gt;

&lt;p&gt;定义一个查找节点的方法，首先使用hash函数计算出下标(哈希地址)，然后根据头地址向下去一个个查找节点，如果节点的key值与目标key值相同，则匹配成功。这里查找的方法因人而已，因为我这里采用的是拉链法来解决哈希冲突，使用一个链表来存储那些冲突的value，所以计算出哈希地址之后也需要遍历列表来查找对应的值。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 查找
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 下标
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// 查找链表
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strcmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;添加节点的方法：&lt;/p&gt;

&lt;p&gt;首先使用lookup方法来查找是否已存在对应的key值，如存在则直接修改value，否则插入一个新的节点，这里需要注意的是哈希冲突，如果哈希地址已存在值，那么将节点的node-&amp;gt;next指向原本的值，并将哈希表node[i]指向新的值。&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 添加
&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashTable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lookup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 该key没有存在
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 创建一个新的节点
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 没有创建成功
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 指向原本的节点
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;小结:&lt;/p&gt;

&lt;p&gt;这是一个比较哈希表比较简单的实现，有很多东西都没有考虑到，但哈希表的基本实现就是这样了，其他无非就是解决哈希冲突和哈希函数的选择，还有当哈希表的填充因子变大时的处理。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/HQL-yunyunyun/HQLHashTable&quot;&gt;GitHub - HQL-yunyunyun/HQLHashTable&lt;/a&gt;&lt;/p&gt;

&lt;h5 id=&quot;参考的博客&quot;&gt;参考的博客&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/snailmann/article/details/80435311&quot;&gt;【数据结构与算法】初入数据结构的哈希表(Hash Table) - 长路漫漫的歇脚处 - CSDN博客&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/s-b-b/p/6208565.html&quot;&gt;数据结构之哈希（hash）表 - {-）大傻逼 - 博客园&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/de33dc676a3f&quot;&gt;哈希表详解（附实现代码） - 简书&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><category term="基础" /><summary type="html">哈希表的概念</summary></entry><entry><title type="html">Gpuimage ‘incomplete filter fbo: 36055’ bug修复记录</title><link href="https://hql-yunyunyun.github.io/GPUImage-Incomplete-filter-FBO-36055-bug%E4%BF%AE%E5%A4%8D%E8%AE%B0%E5%BD%95/" rel="alternate" type="text/html" title="Gpuimage ‘incomplete filter fbo: 36055’ bug修复记录" /><published>2019-04-15T00:00:00+00:00</published><updated>2019-04-15T00:00:00+00:00</updated><id>https://hql-yunyunyun.github.io/GPUImage%20%E2%80%98Incomplete%20filter%20FBO:%2036055%E2%80%99%20bug%E4%BF%AE%E5%A4%8D%E8%AE%B0%E5%BD%95</id><content type="html" xml:base="https://hql-yunyunyun.github.io/GPUImage-Incomplete-filter-FBO-36055-bug%E4%BF%AE%E5%A4%8D%E8%AE%B0%E5%BD%95/">&lt;h2 id=&quot;简述&quot;&gt;简述&lt;/h2&gt;
&lt;p&gt;最近在开发的过程中使用GPUImage导出视频时遇到一个bug，断言在&lt;code class=&quot;highlighter-rouge&quot;&gt;[GPUImageMovieWrite createDataFBO]&lt;/code&gt; 方法中的&lt;code class=&quot;highlighter-rouge&quot;&gt;NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @&quot;Incomplete filter FBO: %d&quot;, status);&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;在google的过程中找到了&lt;a href=&quot;https://github.com/BradLarson/GPUImage/issues/1276&quot;&gt;The current version, the video processing error at the beginning . · Issue #1276 · BradLarson/GPUImage · GitHub&lt;/a&gt;。&lt;/p&gt;

&lt;h2 id=&quot;bug说明&quot;&gt;bug说明&lt;/h2&gt;

&lt;p&gt;这边遇到问题的视频是系统的录屏视频，并通过AVFoundation截取并生成视频前两秒的AVComposition/AVVideoComposition。&lt;/p&gt;

&lt;p&gt;上面给出的解决方法是:&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;	&lt;span class=&quot;n&quot;&gt;AVAsset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anAsset&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AVAsset&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assetWithURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;videoURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;AVAssetTrack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetTrack&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anAsset&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tracksWithMediaType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AVMediaTypeVideo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;objectAtIndex&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_movieFile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GPUImageMovie&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initWithAsset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anAsset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_movieWriter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GPUImageMovieWriter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;alloc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initWithMovieURL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_URLInputVideoFile&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetTrack&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;naturalSize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;anAsset&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;tracksWithMediaType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AVMediaTypeAudio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
	    &lt;span class=&quot;n&quot;&gt;_movieFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audioEncodingTarget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_movieWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;c1&quot;&gt;//no audio
&lt;/span&gt;	    &lt;span class=&quot;n&quot;&gt;_movieFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audioEncodingTarget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;但在我这里，这个方法就行不通了，因为项目里面的 &lt;code class=&quot;highlighter-rouge&quot;&gt;[anAsset tracksWithMediaType:AVMediaTypeAudio].count &amp;gt; 0&lt;/code&gt;。所以我这里的情况就是，新生成的AVComposition是有AudioTrack的，但这AudioTrack偏偏就是没有音频数据的。&lt;/p&gt;

&lt;p&gt;那既然判断AudioTracks.count不是真正的判定是否有音频数据，那么我们只要进一步读取AudioTrack中是否有音频数据那不就OK了？&lt;/p&gt;

&lt;p&gt;VAssetTrack有一个segments的属性，返回track中所有的segment，而AVAssetTrackSegment有一个empty属性:&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/* indicates whether the AVAssetTrackSegment is an empty segment */&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@property&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nonatomic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;readonly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;empty&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;但是在我这边，这个empty是为NO的，还是有数据的。&lt;/p&gt;

&lt;p&gt;一路不通，可以走另外一条路，我们可以创建一个AVAssetReader，直接读取asset的audio数据，这个做法是OK的，但不怎么美观，太粗暴了，直接略过。&lt;/p&gt;

&lt;p&gt;既然从源头过滤问题视频不行，那么我们就从GPUImageWriter中入手，先分析问题出现的原因，再去修复它。&lt;/p&gt;

&lt;h2 id=&quot;bug出现的原因&quot;&gt;bug出现的原因&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Incomplete filter FBO: 36055'&lt;/code&gt;这是系统给出的bug原因，而36055代表的是&lt;em&gt;No images are attached to the framebuffer.&lt;/em&gt;&lt;a href=&quot;https://neslib.github.io/Ooogles.Net/html/0e1349ae-da69-6e5e-edd6-edd8523101f8.htm&quot;&gt;Framebuffer.Status Enumeration&lt;/a&gt;，结合google回来的解决方案，是因为我们给GPUIMageMovie.audioEncodingTarget赋值而导致的问题，那么为什么设置一个audioEncodingTarget会导致创建FBO出现问题呢?&lt;/p&gt;

&lt;p&gt;这个问题先放一边，将关注点放到GPUImageMovie导出的整个流程当中:&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// GPUImageMovie.m
// 这里的方法都是将无关代码删减过后的代码，可以直接查看GPUImage源码
&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 开始asset的数据获取
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;processAsset&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;createAssetReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 创建一个reader
&lt;/span&gt;    &lt;span class=&quot;cm&quot;&gt;/* ...设置videoOutput/audioOutput的一些属性... */&lt;/span&gt;
	 &lt;span class=&quot;c1&quot;&gt;// 开始读取数据并导出 --- 将数据传给GPUImageMovieWriter
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;__unsafe_unretained&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GPUImageMovie&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weakSelf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synchronizedMovieWriter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synchronizedMovieWriter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setVideoInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 设置writer的videoInput回调
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weakSelf&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;readNextVideoFrameFromOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readerVideoTrackOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synchronizedMovieWriter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;setAudioInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 设置writer的audioInput回调
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;weakSelf&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;readNextAudioSampleFromOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readerAudioTrackOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synchronizedMovieWriter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;enableSynchronizationCallbacks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// writer开始写数据
&lt;/span&gt;    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	 &lt;span class=&quot;cm&quot;&gt;/* ...其他操作... */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;// 读取视频的下一帧
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readNextVideoFrameFromOutput&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AVAssetReaderOutput&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readerVideoTrackOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	  &lt;span class=&quot;c1&quot;&gt;// 判断当前reader的状态
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetReaderStatusReading&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;// 获取targetbuffer
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;CMSampleBufferRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sampleBufferRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readerVideoTrackOutput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;copyNextSampleBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sampleBufferRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;cm&quot;&gt;/*
				buffer的时间调整操作
				将targetbuffer传给下一个inputTarget
			*/&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;cm&quot;&gt;/* 判断状态并结束progress */&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synchronizedMovieWriter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;cm&quot;&gt;/* 判断状态并结束progress */&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 读取音频
&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readNextAudioSampleFromOutput&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AVAssetReaderOutput&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readerAudioTrackOutput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c1&quot;&gt;// 判断状态
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetReaderStatusReading&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c1&quot;&gt;// 读取音频 
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;CMSampleBufferRef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioSampleBufferRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readerAudioTrackOutput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;copyNextSampleBuffer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audioSampleBufferRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;cm&quot;&gt;/* 
					属性的设置
					将targetBuffer传给audioEncodingTarget
			   */&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;cm&quot;&gt;/* 判断状态并结束progress */&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;synchronizedMovieWriter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
       &lt;span class=&quot;cm&quot;&gt;/* 判断状态并结束progress */&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;从上面看，这里的关键代码是writer的&lt;code class=&quot;highlighter-rouge&quot;&gt;enableSynchronizationCallbacks&lt;/code&gt;，在这个方法中主导了callback的调用:&lt;/p&gt;
&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// GPUImageMovieWriter.m
// 这里的方法都是将无关代码删减过后的代码，可以直接查看GPUImage源码
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enableSynchronizationCallbacks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;videoInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetWriterStatusWriting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;startWriting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 调用writer的startWriting方法
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterVideoInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestMediaDataWhenReadyOnQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;videoQueue&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;usingBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;cm&quot;&gt;/* ...一些操作... */&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriterVideoInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readyForMoreMediaData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paused&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 循环并读取video数据
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetWriterStatusWriting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
								&lt;span class=&quot;c1&quot;&gt;// video编码结束
&lt;/span&gt;                            &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterVideoInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;markAsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audioInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterAudioInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestMediaDataWhenReadyOnQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audioQueue&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;usingBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;cm&quot;&gt;/* ...一些操作... */&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriterAudioInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readyForMoreMediaData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paused&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;c1&quot;&gt;// 循环并读取音频数据
&lt;/span&gt;                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetWriterStatusWriting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterAudioInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;markAsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;        
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;看码说话，videoInput和audioInput的操作都是相差不大的，都是在block中加入target数据。&lt;/p&gt;

&lt;p&gt;我们来看一下audioInput里面的代码，&lt;code class=&quot;highlighter-rouge&quot;&gt; audioInputReadyCallback &amp;amp;&amp;amp; ! audioInputReadyCallback() &amp;amp;&amp;amp; ! audioEncodingIsFinished&lt;/code&gt;，如果我们的audio数据为空，那么就一定会进入这个if里面调用&lt;code class=&quot;highlighter-rouge&quot;&gt;[assetWriterAudioInput markAsFinished]&lt;/code&gt;结束audio数据的输入，换句话来说就是GPUImageMovieWriter默认只要设置了audio相关的参数，就会默认audio一定有参数，并且当&lt;code class=&quot;highlighter-rouge&quot;&gt;audioInputReadyCallback&lt;/code&gt;回调返回为NO就默认audio数据已输入完毕。&lt;/p&gt;

&lt;p&gt;如果我们给video和audio的block打断点就可以知道audio的回调是比video的回调提前很多这是因为它们两个的数据处理量级都不一致，video需要处理的数据会更大。那么很明显地可以推断出，在第一帧video数据处理之前，&lt;code class=&quot;highlighter-rouge&quot;&gt;[assetWriterAudioInput markAsFinished]&lt;/code&gt;就已经调用了。&lt;/p&gt;

&lt;p&gt;我们回到断言出现的方法中:&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createDataFBO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* ...一些处理... */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GPUImageContext&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;supportsFastTextureUpload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;CVPixelBufferPoolCreatePixelBuffer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterPixelBufferInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;pixelBufferPool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;renderTarget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
		  &lt;span class=&quot;cm&quot;&gt;/* ...一些处理... */&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;cm&quot;&gt;/* ...一些处理... */&lt;/span&gt;
	  &lt;span class=&quot;n&quot;&gt;GLenum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glCheckFramebufferStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GL_FRAMEBUFFER&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;NSAssert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GL_FRAMEBUFFER_COMPLETE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;@&quot;Incomplete filter FBO: %d&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;[assetWriterPixelBufferInput pixelBufferPool]&lt;/code&gt;这个属性有一个特性就是当assetWriter.status != writing就会返回空，那么很明显地，这里因为&lt;em&gt;pixelBufferPool&lt;/em&gt;为空而导致&lt;em&gt;renderTarget&lt;/em&gt;创建失败，进而出现这个bug。&lt;/p&gt;

&lt;p&gt;我们来总结一下这个bug的出现原因:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;只要我们将&lt;em&gt;GPUImageMovie.audioEncodingTarget&lt;/em&gt;赋值给&lt;em&gt;GPUImageMovieWriter&lt;/em&gt;就会生成&lt;em&gt;AudioInput&lt;/em&gt;，进而在开始写入数据时，因为audio的数据为空，所以在创建FBO之前调用了&lt;code class=&quot;highlighter-rouge&quot;&gt;[audioInput markAsFinished]&lt;/code&gt;方法将&lt;em&gt;assetWriter&lt;/em&gt;标志为结束输入，这个时候&lt;em&gt;assetWriter&lt;/em&gt;的状态一直为失败，所以就导致了renderTarget创建失败，进而导致崩溃。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;那么这个也说明为什么只要将&lt;em&gt;GPUImageMovie.audioEncodingTarget&lt;/em&gt;赋值为nil就可以解决静音的bug。&lt;/p&gt;

&lt;h2 id=&quot;bug解决&quot;&gt;bug解决&lt;/h2&gt;

&lt;p&gt;在知道了bug出现的原因之后，解决的方法就有很多了，我们只要保证&lt;code class=&quot;highlighter-rouge&quot;&gt;createDataFBO&lt;/code&gt;方法调用前&lt;code class=&quot;highlighter-rouge&quot;&gt;[audioInput markAsFinished]&lt;/code&gt;不会调用就OK了。&lt;/p&gt;

&lt;p&gt;在这里我选择继承&lt;em&gt;GPUImageMovieWriter&lt;/em&gt;并重写&lt;code class=&quot;highlighter-rouge&quot;&gt;-enableSynchronizationCallbacks&lt;/code&gt;方法，并创建了一个bool值来标志video是否已经开始progress:&lt;/p&gt;

&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#import &quot;GCImageMovieWriter.h&quot;
#import &amp;lt;GPUImage/GPUImage.h&amp;gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@implementation&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GCImageMovieWriter&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoBeganEncoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dispatch_queue_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;@synthesize&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@synthesize&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@synthesize&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paused&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paused&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enableSynchronizationCallbacks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;///!!!: 修复音频轨没有数据的bug
&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// https://github.com/BradLarson/GPUImage/issues/1276
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;videoBeganEncoding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;videoInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetWriterStatusWriting&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;startWriting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;videoQueue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatch_queue_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.sunsetlakesoftware.GPUImage.videoReadingQueue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterVideoInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestMediaDataWhenReadyOnQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;videoQueue&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;usingBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paused&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//NSLog(@&quot;video requestMediaDataWhenReadyOnQueue paused&quot;);
&lt;/span&gt;                &lt;span class=&quot;c1&quot;&gt;// if we don't sleep, we'll get called back almost immediately, chewing up CPU
&lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;usleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//NSLog(@&quot;video requestMediaDataWhenReadyOnQueue begin&quot;);
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriterVideoInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readyForMoreMediaData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paused&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasVideoEncoding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hasVideoEncoding&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;videoBeganEncoding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hasVideoEncoding&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;runAsynchronouslyOnContextQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_movieWriterContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetWriterStatusWriting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;videoEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterVideoInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;markAsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//NSLog(@&quot;video requestMediaDataWhenReadyOnQueue end&quot;);
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audioInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;audioQueue&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dispatch_queue_create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;com.sunsetlakesoftware.GPUImage.audioReadingQueue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterAudioInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;requestMediaDataWhenReadyOnQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;audioQueue&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;usingBlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paused&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;c1&quot;&gt;//NSLog(@&quot;audio requestMediaDataWhenReadyOnQueue paused&quot;);
&lt;/span&gt;                &lt;span class=&quot;c1&quot;&gt;// if we don't sleep, we'll get called back almost immediately, chewing up CPU
&lt;/span&gt;                &lt;span class=&quot;n&quot;&gt;usleep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//NSLog(@&quot;audio requestMediaDataWhenReadyOnQueue begin&quot;);
&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriterAudioInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;readyForMoreMediaData&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_paused&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioInputReadyCallback&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioInputReadyCallback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;runAsynchronouslyOnContextQueue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_movieWriterContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;^&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;assetWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AVAssetWriterStatusWriting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;videoBeganEncoding&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                            &lt;span class=&quot;n&quot;&gt;audioEncodingIsFinished&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;YES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
                            &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;assetWriterAudioInput&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;markAsFinished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
                        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;//NSLog(@&quot;audio requestMediaDataWhenReadyOnQueue end&quot;);
&lt;/span&gt;        &lt;span class=&quot;p&quot;&gt;}];&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;   
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;@end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name></name></author><category term="bug记录" /><category term="基础" /><summary type="html">简述 最近在开发的过程中使用GPUImage导出视频时遇到一个bug，断言在[GPUImageMovieWrite createDataFBO] 方法中的NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @&quot;Incomplete filter FBO: %d&quot;, status);。</summary></entry><entry><title type="html">Objective-C部分总结</title><link href="https://hql-yunyunyun.github.io/Objective-C%E9%83%A8%E5%88%86%E6%80%BB%E7%BB%93/" rel="alternate" type="text/html" title="Objective-C部分总结" /><published>2019-01-01T00:00:00+00:00</published><updated>2019-01-01T00:00:00+00:00</updated><id>https://hql-yunyunyun.github.io/Objective-C%E9%83%A8%E5%88%86%E6%80%BB%E7%BB%93</id><content type="html" xml:base="https://hql-yunyunyun.github.io/Objective-C%E9%83%A8%E5%88%86%E6%80%BB%E7%BB%93/">&lt;h2 id=&quot;简述&quot;&gt;简述&lt;/h2&gt;
&lt;p&gt;这是一篇对OC一些比较基础的归纳。&lt;a href=&quot;https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8A%EF%BC%89.md#%E4%BC%98%E5%8C%96%E9%83%A8%E5%88%86&quot;&gt;参考文章&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;1什么情况使用weak关键字相比assign有什么不同&quot;&gt;1.什么情况使用weak关键字，相比assign有什么不同？&lt;/h2&gt;

&lt;p&gt;使用weak关键字的情况：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;在ARC中，会出现循环引用的情况，这个时候就需要使用weak关键字来修饰，比如delegate属性.&lt;/li&gt;
  &lt;li&gt;自身引用已经对该属性进行过一次强引用了，所以就不需要再使用强引用，如view/IBOutlet控件一般也是用weak
weak和assign的不同点：&lt;/li&gt;
  &lt;li&gt;weak此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时，设置方法既不保留新值，也不释放旧值。此特质同assign类似， 然而在属性所指的对象遭到摧毁时，属性值也会清空(nil out)。 而assign的“设置方法”只会执行针对“纯量类型” (scalar type，例如 CGFloat 或 NSlnteger 等)的简单赋值操作。&lt;/li&gt;
  &lt;li&gt;assign可以用非oc对象，但weak必须用于oc对象。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;2怎么用copy关键字&quot;&gt;2.怎么用copy关键字&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;NSString、NSArray、NSDictionary就一般使用copy关键字，因为他们都有对应的可变类型：
  因为这几个类有对应的可变类型的，如果使用strong修饰，那么有可能在不知情的情况下改变值，所以这里需要用copy来修饰。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;block一般也使用copy关键字：
  block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行：对于 block 使用 copy 还是 strong 效果是一样的，但写上 copy 也无伤大雅，还能时刻提醒我们：编译器自动对 block 进行了 copy 操作。如果不写 copy ，该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”，他们有可能会在调用之前自行拷贝属性值。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;3如何让自己的类用-copy-修饰符如何重写带-copy-关键字的-setter&quot;&gt;3.如何让自己的类用 copy 修饰符？如何重写带 copy 关键字的 setter？&lt;/h2&gt;

&lt;p&gt;若想令自己所写的对象具有拷贝功能，则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本，那么就要同时实现NSCopying与NSMutableCopying协议。
具体步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;需声明该类遵从 NSCopying 协议&lt;/li&gt;
  &lt;li&gt;实现 NSCopying 协议。该协议只有一个方法:
    &lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;copyWithZone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSZone&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;zone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;4property-的本质是什么ivargettersetter-是如何生成并添加到这个类中的&quot;&gt;4.@property 的本质是什么？ivar、getter、setter 是如何生成并添加到这个类中的&lt;/h2&gt;

&lt;p&gt;@property的本质: &lt;em&gt;ivar+getter+setter&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;“属性” (property)作为 Objective-C 的一项特性，主要的作用就在于封装对象中的数据。Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中，“获取方法” (getter)用于读取变量值，而“设置方法” (setter)用于写入变量值。这个概念已经定型，并且经由“属性”这一特性而成为Objective-C 2.0的一部分。 而在正规的 Objective-C 编码风格中，存取方法有着严格的命名规范。 正因为有了这种严格的命名规范，所以 Objective-C 这门语言才能根据名称自动创建出存取方法。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;property&lt;/em&gt;在runtime中的定义&lt;em&gt;objc_property_t&lt;/em&gt;:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objc_property&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objc_property_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;而&lt;em&gt;objc_prperty&lt;/em&gt;是一个结构体：&lt;/p&gt;
&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;property_t&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;attributes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;attributes&lt;/em&gt;在本质上是&lt;em&gt;objc_property_attribute_t&lt;/em&gt;：&lt;/p&gt;
&lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;/// Defines a property attribute
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;           &lt;span class=&quot;cm&quot;&gt;/**&amp;lt; The name of the attribute */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;          &lt;span class=&quot;cm&quot;&gt;/**&amp;lt; The value of the attribute (usually empty) */&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objc_property_attribute_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;而&lt;em&gt;attributes&lt;/em&gt;的具体内容是什么呢？其实，包括：类型，原子性，内存语义和对应的实例变量。&lt;/p&gt;

&lt;p&gt;例如：我们定义一个&lt;em&gt;string&lt;/em&gt;的property &lt;em&gt;@property (nonatomic, copy) NSString *string;&lt;/em&gt;，通过&lt;em&gt;property_getAttributes(property)&lt;/em&gt;获取到&lt;em&gt;attributes&lt;/em&gt;并打印出来之后的结果为&lt;em&gt;T@“NSString”,C,N,V_string&lt;/em&gt;
其中T就代表类型，可参阅 &lt;a href=&quot;https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100-SW1&quot;&gt;Type Encodings&lt;/a&gt; ，C就代表Copy，N代表nonatomic，V就代表对于的实例变量。&lt;/p&gt;

&lt;p&gt;添加了一个属性之后相关的代码会大致生成一下这些内容:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;em&gt;OBJC_IVAR_$&lt;/em&gt;类名&lt;em&gt;$&lt;/em&gt;属性名称：该属性的“偏移量” (offset)，这个偏移量是“硬编码” (hardcode)，表示该变量距离存放对象的内存区域的起始地址有多远。&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;setter&lt;/em&gt; 与 &lt;em&gt;getter&lt;/em&gt; 方法对应的实现函数&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;ivar_list&lt;/em&gt;：成员变量列表&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;method_list&lt;/em&gt;：方法列表&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;prop_list&lt;/em&gt;：属性列表&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在每次新增加一个属性，系统都会在&lt;em&gt;ivar_list&lt;/em&gt;中添加一个成员变量的描述，在&lt;em&gt;method_list&lt;/em&gt;中添加setter和getter方法的描述，在&lt;em&gt;prop_list&lt;/em&gt;添加一个属性的描述，然后计算出该属性在对象中的偏移量，然后给出setter和getter方法对应的实现，在setter方法中从偏移量的位置开始赋值，在getter方法中从偏移量开始取值，为了能够读取正确的字节数，系统对象偏移量的指针类型进行了类型强转。&lt;/p&gt;

&lt;h2 id=&quot;5runtime实现weak属性&quot;&gt;5.runtime实现weak属性&lt;/h2&gt;

&lt;p&gt;weak属性特点:
weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时，设置方法既不保留新值，也不释放旧值。此特质同 assign 类似， 然而在属性所指的对象遭到摧毁时，属性值也会清空(nil out)。&lt;/p&gt;

&lt;p&gt;runtime是如何实现weak变量的自动置为nil?&lt;/p&gt;

&lt;p&gt;runtime 对注册的类， 会进行布局，对于 weak 对象会放入一个 hash 表中。 用 weak 指向的对象内存地址作为 key，当此对象的引用计数为0的时候会 dealloc，假如 weak 指向的对象内存地址是a，那么就会以a为键， 在这个 weak 表中搜索，找到所有以a为键的 weak 对象，从而设置为 nil。&lt;/p&gt;

&lt;h2 id=&quot;6unrecognized-selector异常&quot;&gt;6.unrecognized selector异常&lt;/h2&gt;

&lt;p&gt;这个异常出现的原因很简单:
&lt;code class=&quot;highlighter-rouge&quot;&gt;当调用该对象的上的某个方法时，而该对象没有实现该方法，那么就会报这个错误。&lt;/code&gt;
如何解决:
&lt;code class=&quot;highlighter-rouge&quot;&gt;objc是动态语言，每个方法在运行时都会被动态转为消息转发。objc_msgSend(receiver, selector)&lt;/code&gt;ocjc在向一个对象发送消息时，runtime库会根据对象的ISA指针找到该对象实际所属的类，然后在该类的方法列表以及父类方法列表中寻找方法运行，如果在最顶层的父类方法列表中都没有找到相应的方法是，程序就会抛出异常。&lt;/p&gt;

&lt;p&gt;而objc的运行时给出了三次的拯救机会，现在很多&lt;a href=&quot;https://neyoufan.github.io/2017/01/13/ios/BayMax_HTSafetyGuard&quot;&gt;防止崩溃的库&lt;/a&gt;都是使用这个机制来防止这个异常的出现：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Method resolution：
objc运行时会调用&lt;code class=&quot;highlighter-rouge&quot;&gt;+resloveInstanceMethod:&lt;/code&gt;或者&lt;code class=&quot;highlighter-rouge&quot;&gt;+resolveClassMethod:&lt;/code&gt;，让你有机会提供一个函数实现。如果你添加了函数，那么运行时系统将会重新启动一次消息发送过程，否则，将会移动下一步消息转发(Message Forwarding)。&lt;/li&gt;
  &lt;li&gt;Fast forwarding
如果目标对象实现了&lt;code class=&quot;highlighter-rouge&quot;&gt;-forwardingTargetForSelector:&lt;/code&gt;，那么runtime在这个时候就会调用这个方法，给你把这个消息转发给其他对象的机会。只要返回的不是nil/self，那么整个消息转发机智就会被重启，而转发的对象就是返回的对象。否则将会继续进行下一步Normal Forwarding。&lt;/li&gt;
  &lt;li&gt;Normal forwarding
这是runtime给你挽救的最后一次机会。首先它会发送&lt;code class=&quot;highlighter-rouge&quot;&gt;-methodSignatureForSelector:&lt;/code&gt;消息获得函数的参数和返回值类型。如果&lt;code class=&quot;highlighter-rouge&quot;&gt;-methodSignatureForSelector:&lt;/code&gt;返回nil，Runtime则会发出&lt;code class=&quot;highlighter-rouge&quot;&gt;-doesNotRecognizeSelector:&lt;/code&gt;消息，程序这时也就挂掉了。如果返回了一个函数签名，Runtime就会创建一个NSInvocation对象并发送&lt;code class=&quot;highlighter-rouge&quot;&gt;-forwardInvocation:&lt;/code&gt;消息给目标对象。
    &lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// 调用顺序:
// 1.
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resolveClassMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// 这是class方法
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BOOL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;resloveInstanceMethod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 这是实例方法
// 2.
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forwardingTargetForSelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;aSelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 转发给别的class
// 3.
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;forwardInvocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NSInvocation&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;anInvocation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 生成一个invocation对象
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;7一个obj对象如何进行内存布局考虑有父类的情况&quot;&gt;7.一个obj对象如何进行内存布局？（考虑有父类的情况）&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;所以父类成员变量和自己的成员变量都会存放在该对象所对应的储存空间中。&lt;/li&gt;
  &lt;li&gt;每一个对象内部都有一个isa指针指向他的类对象，类对象中存放着本对象的：
    &lt;ol&gt;
      &lt;li&gt;对象方法列表（对象能够接收的消息列表，保存在它对应的类对象中）&lt;/li&gt;
      &lt;li&gt;成员变量的列表&lt;/li&gt;
      &lt;li&gt;属性列表
它内部也有一个isa指针指向元对象(meta class),元对象内部存放的是类方法列表,类对象内部还有一个superclass的指针,指向他的父类对象。
每个 Objective-C 对象都有相同的结构，如下图所示：
   &lt;em&gt;Objective-C 对象的结构图&lt;/em&gt;
           ISA指针
           根类的实例变量
           倒数第二层父类的实例变量
           …
           父类的实例变量
           类的实例变量&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;根对象就是NSObject，它的superclass指针指向nil&lt;/li&gt;
  &lt;li&gt;类对象既然称为对象，那它也是一个实例。类对象中也有一个isa指针指向它的元类(meta class)，即类对象是元类的实例。元类内部存放的是类方法列表，根元类的isa指针指向自己，superclass指针指向NSObject类。
&lt;img src=&quot;https://raw.githubusercontent.com/HQL-yunyunyun/hql-yunyunyun.github.io/master/post_image/objective-c部分总结_isa指针示意图.png&quot; alt=&quot;isa指针的示意图&quot; title=&quot;isa指针的示意图&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;8objc内存销毁&quot;&gt;8.objc内存销毁&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;调用&lt;code class=&quot;highlighter-rouge&quot;&gt;-release:&lt;/code&gt; ： 引用计数变为0
    &lt;ul&gt;
      &lt;li&gt;对象正在被销毁，生命周期即将结束。&lt;/li&gt;
      &lt;li&gt;不能再有新的 &lt;em&gt;__weak&lt;/em&gt;弱引用，否则将指向nil。&lt;/li&gt;
      &lt;li&gt;调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;[self dealloc]&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;子类调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;-dealloc&lt;/code&gt;：
    &lt;ul&gt;
      &lt;li&gt;继承关系中最底层的子类调用&lt;code class=&quot;highlighter-rouge&quot;&gt;-dealloc&lt;/code&gt;。&lt;/li&gt;
      &lt;li&gt;如果是&lt;em&gt;MRC&lt;/em&gt;代码则会手动释放实例变量们(iVars)。&lt;/li&gt;
      &lt;li&gt;继承关系中每一层的父类都在调用&lt;code class=&quot;highlighter-rouge&quot;&gt;-dealloc&lt;/code&gt;。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;em&gt;NSObject&lt;/em&gt;调用&lt;code class=&quot;highlighter-rouge&quot;&gt;-dealloc&lt;/code&gt;：
    &lt;ul&gt;
      &lt;li&gt;只做一件事：调用&lt;em&gt;Objective-C runtime&lt;/em&gt;中的&lt;code class=&quot;highlighter-rouge&quot;&gt;object_dispose()&lt;/code&gt;方法。&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;调用&lt;code class=&quot;highlighter-rouge&quot;&gt;object_dispose()&lt;/code&gt;:
    &lt;ul&gt;
      &lt;li&gt;为&lt;em&gt;C++&lt;/em&gt;的实体变量们&lt;em&gt;iVars&lt;/em&gt;调用&lt;code class=&quot;highlighter-rouge&quot;&gt;desturctors&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;为&lt;em&gt;ARC&lt;/em&gt;状态下的实体变量们&lt;em&gt;iVars&lt;/em&gt;调用&lt;code class=&quot;highlighter-rouge&quot;&gt;-release&lt;/code&gt;&lt;/li&gt;
      &lt;li&gt;解除所有&lt;em&gt;__weak&lt;/em&gt;引用&lt;/li&gt;
      &lt;li&gt;调用&lt;code class=&quot;highlighter-rouge&quot;&gt;free()&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;9_objc_msg_forward做了什么操作&quot;&gt;9._objc_msg_Forward做了什么操作？&lt;/h2&gt;

&lt;p&gt;对&lt;em&gt;objc-runtime-new.mm&lt;/em&gt; 文件里与&lt;em&gt;_objc_msgForward&lt;/em&gt;有关的三个函数使用伪代码展示下：&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;objc_msgSend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SEL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IMP&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_getMethodImplementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SEL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//调用这个函数，伪代码...
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//查找IMP
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IMP&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;class_getMethodImplementation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SEL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IMP&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lookUpImpOrNil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_objc_msgForward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//_objc_msgForward 用于消息转发
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;IMP&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lookUpImpOrNil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SEL&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_class_initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curClass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;IMP&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//先查缓存,缓存没有时重建,仍旧没有则向父类查询
&lt;/span&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fill_cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache_getImp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;curClass&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;curClass&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;superclass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;imp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;而根据第六个问题中的回答可以得知方法调用的一些步骤如下:&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cm&quot;&gt;/*
结合 《NSObject官方文档》]，排除掉 NSObject 做的事，剩下的就是_objc_msgForward消息转发做的几件事：
1. 调用resolveInstanceMethod:方法 (或resolveClassMethod:)。允许用户在此时为该 Class 动态添加实现。如果有实现了，则调用并返回YES，那么重新开始objc_msgSend流程。这一次对象会响应这个选择器，一般是因为它已经调用过class_addMethod。如果仍没实现，继续下面的动作。
2. 调用forwardingTargetForSelector:方法，尝试找到一个能响应该消息的对象。如果获取到，则直接把消息转发给它，返回非 nil 对象。否则返回 nil ，继续下面的动作。注意，这里不要返回 self ，否则会形成死循环。
3. 调用methodSignatureForSelector:方法，尝试获得一个方法签名。如果获取不到，则直接调用doesNotRecognizeSelector抛出异常。如果能获取，则返回非nil：创建一个 NSlnvocation 并传给forwardInvocation:。
4. 调用forwardInvocation:方法，将第3步获取到的方法签名包装成 Invocation 传入，如何处理就在这里面了，并返回非ni。
5. 调用doesNotRecognizeSelector:，默认的实现是抛出异常。如果第3步没能获得一个方法签名，执行该步骤。

上面前4个方法均是模板方法，开发者可以override，由 runtime 来调用。最常见的实现消息转发：就是重写方法3和4，吞掉一个消息或者代理给其他对象都是没问题的
也就是说_objc_msgForward在进行消息转发的过程中会涉及以下这几个方法：
1. resolveInstanceMethod:方法 (或resolveClassMethod:)。
2. forwardingTargetForSelector:方法
3. methodSignatureForSelector:方法
4. forwardInvocation:方法
5. doesNotRecognizeSelector:方法

正如前文所说：
_objc_msgForward是 IMP 类型，用于消息转发的：当向一个对象发送一条消息，但它并没有实现的时候，_objc_msgForward会尝试做消息转发。
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;10runloop和线程的关系&quot;&gt;10.runloop和线程的关系&lt;/h2&gt;

&lt;p&gt;runloop和线程是紧密相连的，可以说runloop是为了线程而生的。Run loops是线程的基础架构部分， Cocoa 和 CoreFundation 都提供了 run loop 对象方便配置和管理线程的 run loop （以下都以 Cocoa 为例）。每个线程，包括程序的主线程（ main thread ）都有与之相应的 run loop 对象。
runloop和线程的关系:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;主线程的runloop是默认开启的。
在iOS程序中，程序启动后会有一个如下的函数
    &lt;div class=&quot;language-objc highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;err&quot;&gt;@autoreleasepool&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UIApplicationMain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NSStringFromClass&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AppDelegate&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]));&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;UIApplicationMain()&lt;/code&gt;函数就是为&lt;em&gt;main thread&lt;/em&gt;设置一个&lt;em&gt;NSRunloop&lt;/em&gt;对象。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;对于其他线程来说，runloop是默认不开启的。&lt;/li&gt;
  &lt;li&gt;在任何一个cocoa程序的线程中都可以通过以下代码来获得当前线程的runloop:&lt;code class=&quot;highlighter-rouge&quot;&gt;NSRunLoop *runloop = [NSRunLoop currentRunLoop];&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;11runloop详解&quot;&gt;11.runloop详解&lt;/h2&gt;

&lt;p&gt;runloop有一个mode属性，这个属性的主要作用是指定事件在运行循环中的优先级：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;NSDefaultRunLoopMode（kCFRunLoopDefaultMode）：默认，空闲状态&lt;/li&gt;
  &lt;li&gt;UITrackingRunLoopMode：ScrollView滑动时&lt;/li&gt;
  &lt;li&gt;UIInitializationRunLoopMode：启动时&lt;/li&gt;
  &lt;li&gt;NSRunLoopCommonModes（kCFRunLoopCommonModes）：Mode集合
苹果公开提供的 Mode 有两个：&lt;/li&gt;
  &lt;li&gt;NSDefaultRunLoopMode（kCFRunLoopDefaultMode）&lt;/li&gt;
  &lt;li&gt;NSRunLoopCommonModes（kCFRunLoopCommonModes）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;而我们有时候在调用NSTimer时，在滑动页面的时候Timer会暂停回调也是因为runloop的mode属性的原因：
runloop只能运行在一种mode下，如果要换mode，当前的loop也需要停下重启成新的。利用这个机制，ScrollView滚动过程中NSDefaultRunLoopMode（kCFRunLoopDefaultMode）的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动：只能在NSDefaultRunLoopMode模式下处理的事件会影响ScrollView的滑动。&lt;/p&gt;

&lt;p&gt;如果我们把一个NSTimer对象以NSDefaultRunLoopMode（kCFRunLoopDefaultMode）添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换，而导致NSTimer将不再被调度。
同时因为mode还是可定制的，所以：&lt;/p&gt;

&lt;p&gt;Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes（kCFRunLoopCommonModes）来解决。&lt;/p&gt;

&lt;p&gt;一般来说，一个线程一次只能执行一个任务，执行完成后线程就会退出，这就是runloop要做的事情。如果我们需要一个机制，让线程能随时处理事件但并不退出，通常的代码逻辑 是这样的：&lt;/p&gt;
&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;get_next_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;process_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;quit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在objc中，对象通常是通过retainCount的机制来决定是否释放的，在每次runloop的时候，都会检查一遍对象的retainCount，如果为0则将对象释放。&lt;/p&gt;

&lt;h2 id=&quot;12不手动指定autoreleasepool的前提下一个autorealese对象在什么时刻释放比如在一个vc的viewdidload中创建&quot;&gt;12.不手动指定autoreleasepool的前提下，一个autorealese对象在什么时刻释放？（比如在一个vc的viewDidLoad中创建)&lt;/h2&gt;

&lt;p&gt;分两种情况:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;手动干预释放时机 — 指定&lt;em&gt;autoreleasepool&lt;/em&gt;就是所谓的：当前作用域大括号结束时释放；&lt;/li&gt;
  &lt;li&gt;系统自动去释放 — 不手动指定&lt;em&gt;autoreleasepool&lt;/em&gt;。
&lt;em&gt;autorelease&lt;/em&gt;对象出了作用域之后，会被添加到最近一次创建的自动释放池中，并会在当前的&lt;em&gt;runloop&lt;/em&gt;迭代结束时释放。
释放时机可以总结为以下图示：
&lt;img src=&quot;https://raw.githubusercontent.com/HQL-yunyunyun/hql-yunyunyun.github.io/master/post_image/objective-c部分总结_释放时机.png&quot; alt=&quot;释放时机&quot; title=&quot;释放时机&quot; class=&quot;center-image&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;从程序启动到加载完成是一个完整的运行循环，然后会停下来，等待用户交互，用户的每一次交互都会启动一次循环，来处理用户所有的点击事件、触摸事件。&lt;/p&gt;

&lt;p&gt;我们都知道：&lt;strong&gt;所有autorelease的对象，在出了作用域之后，会被自动添加到最近创建的自动释放吃中&lt;/strong&gt;。
但是如果每次都放进应用程序的&lt;em&gt;main.m&lt;/em&gt;中的&lt;em&gt;autoreleasepool&lt;/em&gt;中，迟早会被撑满。在这个过程中必定有一个释放的动作，何时？&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;在一次完整的运行循环结束之前，会被销毁。&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;那什么时间会创建自动释放池？&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;运行循环检测到事件并启动后，就会创建自动释放池。&lt;/code&gt;
子线程的runloop默认是不工作的，无法主动创建，必须得手动创建。&lt;/p&gt;

&lt;p&gt;自定义的&lt;em&gt;NSOperation&lt;/em&gt;和&lt;em&gt;NSThread&lt;/em&gt;需要手动创建自动释放池。比如： 自定义的&lt;em&gt;NSOperation&lt;/em&gt;类中的&lt;em&gt;main&lt;/em&gt;方法里就必须添加自动释放池。否则出了作用域后，自动释放对象会因为没有自动释放池去处理它，而造成内存泄露。
但对于&lt;em&gt;blockOperation&lt;/em&gt;和&lt;em&gt;invocationOperation&lt;/em&gt;这种默认的&lt;em&gt;Operation&lt;/em&gt;，系统已经帮我们封装好了，不需要手动创建自动释放池。&lt;/p&gt;

&lt;p&gt;@autoreleasepool&lt;em&gt;当自动释放池被销毁或者耗尽时，会向自动释放池中的所有对象发送&lt;/em&gt;release&lt;em&gt;消息，释放自动释放池中的所有对象。&lt;/em&gt;
如果在一个&lt;em&gt;vc&lt;/em&gt;的&lt;em&gt;viewDidLoad&lt;/em&gt;中创建一个&lt;em&gt;Autorelease&lt;/em&gt;对象，那么该对象会在&lt;em&gt;viewDidAppear&lt;/em&gt;方法执行前就被销毁了。&lt;a href=&quot;http://blog.sunnyxx.com/2014/10/15/behind-autorelease/&quot;&gt;黑幕背后的Autorelease · sunnyxx的技术博客&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;13block的使用注意点&quot;&gt;13.block的使用注意点&lt;/h2&gt;

&lt;p&gt;循环引用，就是一个对象a强引用了对象b，而b又强引用了对象a，这样就会造成循环引用，尤其在使用block的时候得注意这个问题。&lt;/p&gt;

&lt;p&gt;如果对象a持有了一个block，而又在block中强引用了对象a(一般是调用self、或者是属性)，那么就会形成循环引用，为什么呢？&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;因为block都会对block内的对象进行捕获，并会强引用&lt;a href=&quot;https://juejin.im/post/5b0181e15188254270643e88#heading-12&quot;&gt;iOS底层原理总结 - 探寻block的本质（一） - 掘金&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;block内如何修改block外部变量？为什么？&lt;/p&gt;

&lt;p&gt;可以使用&lt;em&gt;__block&lt;/em&gt; 去修饰外部变量。&lt;/p&gt;

&lt;p&gt;我们都知道：&lt;em&gt;Block&lt;/em&gt;不允许修改外部变量的值，这里所说的外部变量的值，指的是栈中指针的内存地址。&lt;em&gt;__block&lt;/em&gt;所起到的作用就是只要观察到该变量被 &lt;em&gt;block&lt;/em&gt; 所持有，就将“外部变量”在栈中的内存地址放到了堆中。进而在&lt;em&gt;block&lt;/em&gt;内部也可以修改外部变量的值。&lt;/p&gt;</content><author><name></name></author><category term="Objective-C" /><category term="基础" /><summary type="html">简述 这是一篇对OC一些比较基础的归纳。参考文章</summary></entry></feed>