0%

关于 UIView 的透视度,有四个属性(下图中红框中的项)都与之相关,作为一个喜欢刨根问底的程(处)序(女)员(座),一定要搞清楚它们各自的用处跟区别是什么呢?

hidden

此属性为 BOOL 值,用来表示 UIView 是否隐藏。关于隐藏大家都知道就是让 UIView 不显示而已,但是需要注意的是:

  • 当前 UIView 的所有 subview 也会被隐藏,忽略 subview 的 hidden属性。UIView 中的 subview 就相当于 UIView 的死忠小弟,老大干什么我们就跟着老大,同进同退,生死与共!
  • 当前 UIView 也会从响应链中移除。你想你都不显示了,就不用在响应链中接受事件了。

alpha

此属性为浮点类型的值,取值范围从 0.0 到 1.0,表示从完全透明到完全不透明,其特性有:

  • 当前 UIView 的 alpha 值会被其所有 subview 继承。因此,alpha 值会影响到 UIView 跟其所有 subview。
  • alpha 具有动画效果。当 alpha 为 0 时,跟 hidden 为 YES 时效果一样,但是 alpha 主要用于实现隐藏的动画效果,在动画块中将 hidden 设置为 YES 没有动画效果。

backgroundColor 的 alpha(Clear Color)

此属性为 UIColor 值,而 UIColor 可以设置 alpha 的值,其特性有:

  • 设置 backgroundColor 的 alpha 值只影响当前 UIView 的背景,并不会影响其所有 subview。这点是同 alpha 的区别,Clear Color 就是 backgroundColor 的 alpha 为 1.0。
  • alpha 值会影响 backgroundColor 最终的 alpha。假设 UIView 的 alpha 为 0.5,backgroundColor 的 alpha 为 0.5,那么 backgroundColor 最终的 alpha 为 0.25(0.5 乘以 0.5)。

opaque

此属性为 BOOL 值。要搞清楚这个属性的作用,就要先了解绘图系统的一些原理:屏幕上的每个像素点都是通过 RGBA 值(Red、Green、Blue 三原色再配上 Alpha 透明度)表示的,当纹理(UIView 在绘图系统中对应的表示项)出现重叠时,GPU 会按照下面的公式计算重叠部分的像素(这就是所谓的“合成”):

Result = Source + Destination * (1 - SourceAlpha)

Result 是结果 RGB 值,Source 为处在重叠顶部纹理的 RGB 值,Destination 为处在重叠底部纹理的 RGB 值。通过公式发现:当 SourceAlpha 为 1 时,绘图系统认为下面的纹理全部被遮盖住了,Result 等于 Source,直接省去了计算!尤其在重叠的层数比较多的时候,完全不同考虑底下有多少层,直接用当前层的数据显示即可,这样大大节省了 GPU 的工作量,提高了效率。(多像现在一些“美化墙”,不管后面的环境多破烂,“美化墙”直接遮盖住了,什么都看不到,不用整治改进,省心省力)。更详细的可以读下 objc.io 中<绘制像素到屏幕上>这篇文章。

那什么时候 SourceAlpha 为 1 呢?这时候就是 opaque 上场的时候啦!当 opaque 为 YES 时,SourceAlpha 为 1。opaque 就是绘图系统向 UIView 开放的一个性能开关,开发者根据当前 UIView 的情况(这些是绘图系统不知道的,所以绘图系统也无法优化),将 opaque 设置为 YES,绘图系统会根据此值进行优化。所以,如果在开发时某 UIView 是不透明的,就将 opaque 设置为 YES,能优化显示效率。

需要注意的是:

  1. 当 UIView 的 opaque 为 YES 时,其 alpha 必须为 1.0,这样才符合 opaque 为 YES 的场景。如果 alpha 不为 1.0,最终的结果将是不可预料的(unpredictable)。
  2. opaque 只对 UIView 及其 subclass 生效,对系统提供的类(像UIButton,UILabel)是没有效果的。

参考

为什么进行图片拉伸

  1. 灵活:对于一些带边框的背景图而言,如果不进行图片拉伸,每当改变 frame 时就需要一份新 size 的背景图
  2. 节约资源:对于一些比较规则的图片,没必须使用 frame 大小,而是使用一个满足拉伸条件的较小的图片进行拉伸,这样加载图片变快,而且图片大小变小

使用方法

使用 - resizeableImageWithCapInsets:(UIEdgeInsets)capInsets,iOS 官方解释如下:

You use this method to add cap insets to an image or to change the existing cap insets of an image. In both cases, you get back a new image and the original image remains untouched.
During scaling or resizing of the image, areas covered by a cap are not scaled or resized. Instead, the pixel area not covered by the cap in each direction is tiled, left-to-right and top-to-bottom, to resize the image. This technique is often used to create variable-width buttons, which retain the same rounded corners but whose center region grows or shrinks as needed. For best performance, use a tiled area that is a 1x1 pixel area in size.

原理就是对 UIImage 指定了 Insets 后,在 Insets 中的区域不进行拉伸,只对区域外的部分进行拉伸。需要注意的有两点:

  1. UIEdgeInsetsMake 的四个参数从前到后依次是 top、left、bottom、right,不是成对的,别搞错了
  2. 拉伸的方式不是全方向一起拉伸!而是先左右拉伸,再上下拉伸,最后的结果是两次拉伸的叠加!不然会出现这样的问题

参考

目前我见到的所有芯片中都含有 PLL 模块,而且一直不知道如何利用 PLL 对晶振进行倍频的,这次利用维基百科好好的学习了下 PLL 的原理。

1. 时钟与振荡电路

在芯片中,最重要的就是时钟,时钟就像是心脏的脉冲,如果心脏停止了跳动,那人也就死亡了,对于芯片也一样。了解了时钟的重要性,那时钟是怎么来的呢?时钟可以看成周期性的0与1信号变化,而这种周期性的变化可以看成振荡。因此,振荡电路成为了时钟的来源

振荡电路的形成可以分两类:

  1. 石英晶体的压电效应:电导致晶片的机械变形,而晶片两侧施加机械压力又会产生电,形成振荡。它的谐振频率与晶片的切割方式、几何形状、尺寸有关,可以做得精确,因此其振荡电路可以获得很高的频率稳定度。
  2. 电容 Capacity 的充电放电:能够存储电能,而充放电的电流方向是反的,形成振荡。可通过电压等控制振荡电路的频率。

2. PLL与倍频

由上面可以知道,晶振由于其频率的稳定性,一般作为系统的外部时钟源。但是晶振的频率虽然稳定,但是频率无法做到很高(成本与工艺限制),因此芯片中高频时钟就需要一种叫做压控振荡器(Voltage Controlled Oscillator)的东西生成了(顾名思义,VCO 就是根据电压来调整输出频率的不同)。可压控振荡器也有问题,其频率不够稳定,而且变化时很难快速稳定频率。哇偶,看到这种现象是不是很熟悉?嘿嘿,这就是标准开环系统所出现的问题,解决办法就是接入反馈,使开环系统变成闭环系统,并且加入稳定的基准信号,与反馈比较,以便生成正确的控制。

PLL

因此,为了将频率锁定在一个固定的期望值,锁相环PLL出现了!一个锁相环PLL电路通常由以下模块组成:

  • 鉴相鉴频器 PFD(Phase Frequency Detector):对输入的基准信号(来自频率稳定的晶振)和反馈回路的信号进行频率的比较,输出一个代表两者差异的信号
  • 低通滤波器 LPF(Low-Pass Filter):将PFD中生成的差异信号的高频成分滤除,保留直流部分
  • 压控振荡器 VCO(Voltage Controlled Oscillator):根据输入电压,输出对应频率的周期信号。利用变容二极管(偏置电压的变化会改变耗尽层的厚度,从而影响电容大小)与电感构成的 LC 谐振电路构成,提高变容二极管的逆向偏压,二极管内耗尽层变大,电容变小,LC 电路的谐振频率提高,反之,降低逆向偏压时,二极管内电容变大,频率降低
  • 反馈回路FL(Feedback Loop):通常由一个分频器实现。将 VCO 的输出降低到与基准信号相同级别的频率才能在 PFD 中比较

PLL 工作的基本原理就是将压控振荡器的输出经过分频后与基准信号输入 PFD,PFD 通过比较这两个信号的频率差,输出一个代表两者差异的信号,再经过低通滤波器转变成一个直流脉冲电压去控制 VCO 使它的频率改变。这样经过一个很短的时间,VCO 的输出就会稳定下来。所以:

PLL 并不是直接对晶振进行倍频,而是将频率稳定的晶振作为基准信号,与 PLL 内部振荡电路生成的信号分频后进行比较,使 PLL 输出的信号频率稳定

最后,根据原理,理解一下锁相环(Phase Locked Loop)的名称

  • 为了对基准信号与反馈信号进行频率比较,二者的相位必须相同且锁住,任何时间都不能改变,这样才能方便的比较频率,所以叫锁相(Phase Locked)
  • 为了快速稳定输出系统,整个系统加入反馈成为闭环,所以叫环(Loop)

参考文档

什么是傅立叶变换

关于傅立叶变换,可以用果汁牛奶打一个比方:

  • 什么是傅立叶变换:对于一杯果汁牛奶,能分析出其食谱(recipe)
  • 如何进行傅立叶变换:运行果汁牛奶的过滤器(filters),将各类成分(ingredient)分离(extract)出来
  • 为什么要进行傅立叶变换:食谱要比果汁牛奶要容易分析、比较、修改得多
  • 如何再变换回去:根据食谱将成分再混合到一起,就又得到果汁牛奶了

Smoothie to recipe

总之,对果汁牛奶进行傅立叶变换后,我们的视角(perspective)从消费者变成了生产者,从 “What did i see” 变成了 “How was it made”,Recipe 比 Object 本身更容易进行分析、比较、修改。

所谓“变换”,就是换个领域看问题(A Change Of Perspective),将某种情况下不易分析处理的领域换成易分析处理的领域

好,让我们看看真正的傅立叶变换,维基百科上的一张GIF图清晰明了,可以看出:

fft

  1. 任何连续周期信号都可以由一组适当的正弦曲线组合而成(这些正弦曲线通过叠加逼近,直至误差可以忽略)
  2. 连续周期信号的表象是时域(time domain)波形,从时域上,我们很难进行分析、处理,因此我们需要从另外一个维度去分析信号,而傅立叶变换就是将信号从时域(Observations In The Time Domain)转换到频域(Ingredients In The Frequency Domain)。为什么要到频域呢?频率只是信号的一个特征,但是它可以用来识别信号,在频域可以得到信号的成分(ingredients),就像果汁牛奶一样,Recipe 比 Object 本身更容易分析、比较、修改

分离信号与傅立叶变换的应用

从上面可以看出,傅立叶变换说到底就是分离信号,可为什么要分离信号呢?其实信号只是信息的载体,信息无处不在且多种多样,因此信号的类型也多种多样,但是在传输的方式却比较少(每一种的容量非常大),例如声音、光线、电磁等等。由于同一种传输方式的容量非常大,将信道分成不同区域同时使用,信道的利用率大大的提高了。

但是问题来了:虽然对信道进行划分,但是信号在相同的信道中传输时,大自然不管你如何划分,都是按同样的方式传输,不同类型的信号被合在一起进行传输,就像上面的果汁牛奶,不管你把各类成分划分得如何清晰,果汁机出来就是混合过的果汁。这时,接受端就需要对信号进行分离,识别出不同的信号,进行分析处理。分离信号是我们最常见的事情,我们无时无刻不在进行,以至于根本没有注意到。例如,看到不同的颜色,尝到不同的味道,听到不同的声音(对于振动产生的声音,在传播时不管高低音,是缠在一起在介质中传输的,但是人耳可以分辨出)。

傅立叶变换只是分离信号方法中的一种(靠频率分离),由此可以想到傅立叶变换的一些应用有:

  • 滤波:将不需要的频率对应的信号过滤掉,只留下需要的信号
  • 调频:基于滤波,只专注于特定频率的信号进行接受
  • 去噪:通过分离,把噪声对应频率的信号处理掉,增强核心的信号
  • 压缩:将那些不太重要的部分忽略掉,保留主要部分(有损压缩)。例如,JPEG对.bmp压缩,MP3对.wav压缩都是这种方式

参考资料

我喜欢那种深入浅出,生动有趣的资料,而不是死板的公式与定理,所以下面的资料都非常有趣,看完能很快对傅立叶变换有初步的理解:

最近关于开题的事情搞得焦头烂额,我对论文的想法是尽量锻炼自己的工程能力,将研究生阶段所学的东西都利用了,个人感觉这才叫工程硕士。而实习单位的老师就是一定要跟单位方向结合,要有创新。观念上有冲突,很难开题。

有人会说,还是做一些特别有研究方向的吧,这样才有意义。其实要是想有意义,工作之后有的是机会,从事的工作都是特别有用的,面向的都是几十万几百万甚至千万的用户,哪个不比所谓的科研项目有意义,这才几个用户,才能服务多少人?大学之所以能称为象牙塔,就是自由!工作之后是螺丝钉,技术面被大大限制(虽然深度有所加深,但技术广度往往对技术深度提升有很大帮助)。而在学校,没有任何限制,肆意发挥,想学什么就学什么,想用什么技术就用什么技术(这里不是指就可以不考虑代码的专业性,代码还是越接近工业级越好),允许你去尝试去试错,这样才能在工作后游刃有余。这就是为什么要在综合实践 JoyCar 中做那么多平台,从 STM32 无 OS 到 ARM+Linux,从 Framebuffer 到 Qt 到 iOS。在学校,一定要去做那些工作之后没时间、没精力、没条件做的东西,学校一旦毕业就很难再回去,这才是在学校需要好好珍惜的东西!

有人会说,研究生,就一定要在某个方向上有很深的积累,这样才有竞争力。我一开始也是这样认为的,一定要有个研究方向。可我后来发现我理解错了,方向不一定指研究方向。是,在图像处理、安全等有很深的积累,确实非常有竞争力,可我是工程硕士,跟学术硕士比这点有先天的差距,他们天天读 paper,接触的是最前沿,我即使追赶也比较难追赶上,即使追赶上也很难拉开差距。后来我想通了,我在专业相关的基础知识上更深入一点点、积累多一点点、对课程知识多掌握一点点,这些一点点汇总起来,是不是也是种竞争力呢?而且我相信这种竞争力不简简单单是 1 + 1 = 2 的效果,而是能够互相影响互相强化的。强化这种竞争力,很乐而不为?

总之,我对自己工程硕士一直有个要求,就是:夯实基础、加强动手能力。但一直以来这个要求很抽象,很难具体表述出来,直到今天看到一个博客上 About Me 的说明,特别有感触:

About Me

I Can Bring Ideas Into Reality!

为什么选择Octopress

我一直是一个喜欢做笔记的人,觉得做笔记有以下优点:

  • 在阅读时能够很好的帮助我思考,提炼精华,把握核心,在复习总结时非常有用
  • 能把一些零散的知识通过分类给聚合起来,形成知识体系
  • 每次看到满满一本全是笔记的时候,非常有成就感

之前一直是在用笔在本子上做笔记,比较喜欢自由涂鸦的那种感觉,但渐渐的发现了一些问题:

  1. 纸制的保存时间有限,而且易丢失
  2. 修改扩展起来比较麻烦
  3. 检索、聚合起来不方便

后来试过像 CSDN 之类的博客,总觉得不好用,尤其是文本编辑,感觉非常麻烦, Vim 用多了,喜欢纯文本编辑的乐趣与效率,搜索过自带格式的语言,HTML 更适合展示,大量的格式信息把文本“淹没”了,而 Latex 又过于复杂,直到通过 Github 学习了 Markdown,终于找到了一个适合写作的语言了,在用 Markdown 写作时有种开心的感觉,就像拿 LAMY 的钢笔在 MUJI 的本子上写作一样,各种爱不释手,哈哈。为什么这么喜欢 Markdown呢?我觉得有以下原因:

  • 纯文本,所以兼容性强,可以用文本编辑器打开
  • Markdown 的标记语法简单清晰,具有很好的可读性,并且不会影响文本
  • 专注于文字而不是排版
  • 格式转换非常方便,可以轻松转换成 HTML、PDF 等

用惯了 Markdown 之后开始 YY,既然 Markdown 转换 HTML 这么容易,如果能有支持 Markdown 的博客就好了,于是我 Google 了一下,发现了 Octopress,一个非常酷的博客框架,而且可以 deploy 到 Github Pages 上,我擦,Github + Markdown,这一下子击中了我,所以立马用 Octopress 搭建了一个博客,以后要渐渐将思考、笔记记录到这个博客上了。

专注+坚持,一定能够有所收获!

下面就介绍下如何通过 Octopress 搭建 Blog:

Octopress搭建原理

Octopress 的文档上关于搭建步骤已经写的非常详细了,所以这里就不再重复。我是一个喜欢刨根问底的人(处女座),所以对搭建跟使用过程中的这些疑问很感兴趣:

  • 在搭建过程中 gem/bundle/rake 都是干什么的,为什么 rake cmd 就执行相关操作?
  • 为什么 deploy 到 Github 上的目录跟本地目录不一样(为什么有 source 跟 master 两个分支)?

搭建步骤原理

对于第一个问题,Octopress 是基于 ruby 的,因此 gem/bundle/rake 都是 ruby 相关的一些工具,可以看一下整理 Ruby 相关的各种概念这篇文章了解下:

  • Gem:ruby 中的包管理器(Package Manager),就如同 Ubuntu 中的 apt-get,方便管理与使用各类 ruby 包。但是一个项目有可能用很多包,总不能一个一个手动用 Gem 获取吧?于是 Bundle 出现了:
  • Bundle:相当于多个 Gem 批处理运行。在配置文件 Gemfile 里说明应用依赖哪些第三方包,Bundle 自动帮你下载安装多个包,并且会下载这些包依赖的包。
  • Rake:Ruby Make,顾名思义,一个用 ruby 开发的代码构建工具。由于 ruby 不需要编译,所以 Rake 其实相当于一个任务管理工具:
    1. 以任务的方式创建和运行脚本(Rakefile)
    2. 追踪和管理任务间的依赖

所以我们就理解了安装步骤:

  1. 首先用 gem install bundler 安装 Bundle
  2. 通过 bundle install,Bundle 读取 Gemfile 中 Octopress 需要的包,进行下载安装
  3. 通过 rake cmd,Rake 读取 Rakefile 脚本中的内容,执行对应的任务

Octopress部署框架

对于第二个问题,Octopress 是一个生成器,它根据你的配置与主题,将 Markdown 格式的文档转化成 HTML 页面,生成出一个完成 Blog(在 _deploy 目录中),所以应该将生成器与 Markdown 编写的内容同生成的 Blog 网页分开,于是在 Github 上存在两个分支:

  • master:生成的 Blog 是期望访问 username.github.io 就访问到的,因此利用 Github 默认显示的 master 分支的特性,将生成的 Blog 设置为 master
  • source:Octopress 生成器与用 Markdown 编写的内容放置在 source 分支

这样,对多台电脑同步 Octopress 博客的操作也明白为什么要这样做了:

Octopress 多台电脑同步方法

建立本地 Octopress 仓库,操作如下:

1
2
3
$ git clone -b source https://github.com/username/username.github.io octopress //先 clone 生成器与文章
$ cd octopress // 生成的 Blog 是被放置在生成器的一个子目录中的,因此要进入生成器目录
$ git clone https://github.com/username/username.github.io _deploy // 然后再 clone 所生成的内容

更新

1
2
3
4
$ cd octopress
$ git pull origin source # 更新 local 的 source 分支
$ cd _deploy
$ git pull origin master # 更新 local 的 master 分支

推送

1
2
3
4
5
$ rake generate
$ git add .
$ git commit -m "Commit Message"
$ git push origin source # 更新 remote 的 source 分支
$ rake deploy # 更新 remote 的 master 分支

Octopress 搭建过程中问题总结

1. You have already activated rake 0.9.0, but your Gemfile requires rake 0.8.7

说明 Rake 版本不匹配,使用 bundle update rake 更新下 Rake 即可,具体见这里

2. 如何在侧边栏显示 Github 仓库

仅仅在 _config.yml 中将 github_user 填上 Github 的用户名是不行的,还需要修改 default_asides,将 asides/github.html 加入。