Button是我们平时用的最多的控件之一,经常会遇到图片文字位置的问题,用下面的方法一行代码就可以搞定
创建UIButton分类
.h
|
|
.m
|
|
实现
|
|
雪忆, 如雪般单纯, 冷静思考.
|
|
|
|
|
|
在VSync信号到来后,系统图形服务会通过CADisplayLink等机制通知App,App主线程开始在CPU中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后CPU会将计算好的内容提交到GPU去,由GPU进行变换、合成、渲染。随后GPU会把渲染结果提交到帧缓冲区去,等待下一次VSync信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个VSync时间内,CPU或者GPU没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。
在开发中,CPU和GPU中任何一个压力过大,都会导致掉帧现象(FPS< 60)
加载资源,对象创建,对象调整,对象销毁,布局计算,Autolayout,文本计算,文本渲染,图片的解码, 图像的绘制(Core Graphics)都是在CPU上面进行的。
1、对象的创建:
对象的创建会分配内存、设置属性等,会消耗CPU资源。所以尽量使用轻量对象代替,比如能用CALayer的时候尽量不用UIView,敏感位置能不用IB尽量使用纯代码手写。
推迟同一时间创建对象,推荐使用懒加载在需要使用时候创建对象。
2、对象调整
对 UIView 的这些属性进行调整时,消耗的资源要远大于一般的属性。对此你在应用中,应该尽量减少不必要的属性修改。
当视图层次调整时,UIView、CALayer 之间会出现很多方法调用与通知,所以在优化性能时,应该尽量避免调整视图层次、添加和移除视图。
3、对象销毁
当前类持有大量对象时候,其销毁时候的资源消耗就非常明显。建议创建销毁的异步队列,将需要销毁的对象放到队列中销毁。
4、布局计算
布局计算在UITableView使用中是最常见的消耗资源的地方。建议取到数据之后,异步进行计算布局并缓存下来,当复用Cell时候直接调用缓存数据。
5、AutoLayout
Autolayout 对于复杂视图来说常常会产生严重的性能问题,AutoLayout相对低效的原因是隐藏在底层的命名为”Cassowary“的约束求解系统,随着视图数量的增长,Autolayout 带来的 CPU 消耗会呈指数级上升,当Cell内约束超过25个的时候,会降低滑动的帧率。
6、文本的计算以及渲染
UI中存在大量的对于文本高度的适配,可以参考:用 [NSAttributedString boundingRectWithSize:options:context:] 来计算文本宽高,用 -[NSAttributedString drawWithRect:options:context:] 来绘制文本。尽管这两个方法性能不错,但仍旧需要放到后台线程进行以避免阻塞主线程。
常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。解决办法是利用TextKit或者是CoreText自定义文本控件。详见:YYText。
7、图片解码以及图像的绘制
当你用 UIImage 或 CGImageSource 的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。如果想要绕开这个机制,常见的做法是在后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap 直接创建图片。目前常见的网络图片库都自带这个功能。
个最常见的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是线程安全的,所以图像的绘制可以很容易的放到后台线程进行。
8、文件系统的调用
NSFileManager获取一个目录获取文件信息,进行多次递归计算,stat几乎瞬间完成,NSFileManager耗时较长且消耗CPU。
GPU是一个专门为图形高并发计算而量身定做的处理单元,比CPU使用更少的电来完成工作并且GPU的浮点计算能力要超出CPU很多。
GPU的渲染性能要比CPU高效很多,同时对系统的负载和消耗也更低一些,所以在开发中,我们应该尽量让CPU负责主线程的UI调动,把图形显示相关的工作交给GPU来处理,当涉及到光栅化等一些工作时,CPU也会参与进来,这点在后面再详细描述。
相对于CPU来说,GPU能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合(合成)并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。
1、纹理的渲染
当在较短时间显示大量图片时(比如 TableView 存在非常多的图片并且快速滑动时),CPU 占用率很低,GPU 占用非常高,界面仍然会掉帧。避免这种情况的方法只能是尽量减少在短时间内大量图片的显示,尽可能将多张图片合成为一张进行显示。
2、视图的混合(Blended)
视图结构过于复杂,混合的过程、会消耗很多 GPU 资源。为了减轻这种情况的 GPU 消耗,应用应当尽量减少视图数量和层次,并在不透明的视图里标明 opaque 属性以避免无用的 Alpha 通道合成。当然,这也可以用上面的方法,把多个视图预先渲染为一张图片来显示。
Blended Layers(视图混合):在同一个区域内,存在着多个有透明度的图层,那么GPU需要更多的计算,混合上下多个图层才能得出最终像素的RGB值。
Misaligned Images(像素对齐):逻辑像素(point)和 物理像素(pixel)无法相匹配;图片的size和显示图片的imageView的size(逻辑像素(point))不相等。
3、图形的生成
CALayer 的 border、圆角、阴影、遮罩(mask),CASharpLayer 的矢量图形显示,通常会触发离屏渲染(offscreen rendering),而离屏渲染通常发生在 GPU 中。可以尝试开启 CALayer.shouldRasterize 属性,但这会把原本离屏渲染的操作转嫁到 CPU 上去。
好的方法是使用图片遮罩等方法,避免使用圆角和隐形等。详细:iOS的离屏渲染
解决方案:
1、预先计算UI布局
获取数据之后,异步计算Cell高度以及各控件高度和位置,并储存在CellLayouModel中,当每次Cell需要高度以及内部布局的时候就可以直接调用,不需要进行重复计算。
2、使用自动缓存高度
iOS 8之后出现了UITableView通过约束自动计算高度,但是因为iOS对于约束的算法问题,会导致流畅性降低,FDTemplateLayoutCell很好的优化了这个问题。
3、异步绘制
Facebook的开源项目Texture(原AsyncDisplayKit),通过利用ASDisplayNode封装了CALayer,实现了异步绘制。
第三方微博客户端墨客的是现实,当滑动时,松开手指后,立刻计算出滑动停止时 Cell 的位置,并预先绘制那个位置附近的几个 Cell,而忽略当前滑动中的 Cell。但也有缺点,快速滑动的时候有可能会出现大量空白。
3、高效图片加载
4、预加载
列表当中,当滑动到一个可以设定的位置的时候,提前获取下载下一页的数据,并绘制UI。详见:https://zhuanlan.zhihu.com/p/23418800。
5、针对Blended Layers以及Misaligned Images
Blended Layers:
尽量少的使用具有透明色的图片
尽量多的将UI部件增加背景色
减少同一像素点进行过多的颜色计算
Misaligned Images:
现象:
洋红色:UIView的frame像素不对齐,即不能换算成整数像素值。
黄色:UIImageView的图片像素大小与其frame.size不对齐,图片发生了缩放造成。
解决:
尽量使用ceil(),保证没有小数的UI绘制
尽量不实用0.01f标记UITableView或者UICollectionView的header以及footer
网络上获取的图片没有@2x和@3x的区别,需要我们缩放图片到与UIImageView对应的尺寸,且缩放后的图片的scale和[UIScreen mainScreen].scale相等,再显示出来。
其他:
下面的情况或操作会引发离屏渲染:
为图层设置遮罩(layer.mask)
将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
为图层设置阴影(layer.shadow *)。
为图层设置layer.shouldRasterize=true
具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层
文本(任何种类,包括UILabel,CATextLayer,Core Text等)。
使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。
//这个方法是在controller的类在IB中创建,但是是通过Xcode实例化controller的时候调用的.
//一般情况下调用 init 方法或者调用 initWithNibName 方法的实例化UIViewController,不管调用哪个方法都会调用initWithNibName。
/
如果是通过调用initWithNibName:bundle指定nib文件名初始化的话,ViewController会根据此nib来创建View。如果name参数为nil,则ViewController会通过以下两个步骤找到与其关联的nib:
1)找跟ViewController类名一样的文件,例如MyViewController,则查找是否存在MyViewController.nib
2)如果ViewController的类名以“Controller”结尾,例如ViewController的类名是MyViewController,则查找是否存在MyView.nib;
/
-(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
当访问 UIViewController 的 view 属性时,view 如果此时是 nil,那么 VC 会自动调用 loadView 方法来初始化一个 UIView 并赋值给 view 属性。如果没有重载 loadView 方法,则 UIViewController 会从 nib 或 StoryBoard 中查找默认的 loadView ,默认的 loadView 会返回一个空白的 UIView 对象。
此方法在控制器的 view 为 nil 的时候被调用。 此方法用于以编程的方式创建 view 的时候用到。loadView 是使用代码生成视图的时候,当视图第一次载入的时候调用的方法。用于使用(写)代码来实现控件。用于使用代码生成控件的函数
需要注意的是,在view初始化之前,不能先调用view的getter方法,否则将导致死循环(除非先调用了[supper loadView]);
如果 loadView 不能生成 UIViewController.view, 系统将会反复调用 loadView 及 viewDidLoad 方法, 并且最终调用 [super loadView] 方法返回 UIViewController.view。
当VC的view对象载入内存后调用,用于对view进行额外的初始化操作 。
视图即将可见时调用(这个时候 supperView 还是 nil,即显示动画切换之前调用 )。默认情况下不执行任何操作。
这个方法中完成任何与视图显示相关的任务,例如改变视图方向、状态栏方向、视图显示样式等。
view 即将布局其 Subviews。比如 view 的 bounds 改变了(例如状态栏从不显示到显示,视图方向变化),要调整 Subviews 的位置,在调整之前要做的一些工作就可以在该方法中实现。
view 已经布局其 Subviews。比如 view 的 bounds 改变了(例如状态栏从不显示到显示,视图方向变化),已经调整 Subviews 的位置,在调整完成之后要做的一些工作就可以在该方法中实现。
视图已完全过渡到屏幕上时调用。在这个方法中执行视图显示相关附件任务,如果重载了这个方法,必须在方法中调用 [supper
viewDidAppear]。
view 即将从 superView 中移除且移除动画切换之前,此时还没有调用 removeFromSuperview。
view从superView中移除,移除动画切换之后调用,此时已调用removeFromSuperview。
当系统的内存不足时,该方法会被调用,我们可以在该方法里释放掉部分暂时不用的资源。
viewController 最后的清理工作,释放视图对象的引用和 viewController 所需数据结构。
|
|
|
|
###//返回当前进程的信息
###//以NSString对象数组的形式返回当前进程的参数
###//返回变量/值对词典,以描述当前的环境变量(比如PATH和HOME)及其值
###//返回进程标识符,它是操作系统赋予进程的唯一数字,用于识别每个正在运行的进程
###//返回当前正在执行的进程名称
###//每次调用这个方法时,都返回不同的单值字符串,可以用这个字符串生成单值临时文件名
###//返回主机系统的名称
|
|
###//返回表示操作系统的数字
|
|
###//返回操作系统的名称(可能返回的值定义在NSProgressInfo.h文件中)
###//返回操作系统的当前版本
|
|
###//将当前进程名称设置为name。应该谨慎地使用这个方法,应为关于进程名称存在一些假设(比如用户默认的设置)
什么是LLDB呢:它是一个有着 REPL 的特性和 C++ ,Python 插件的开源调试器。LLDB 绑定在 Xcode 内部,存在于主窗口底部的控制台中。调试器允许你在程序运行的特定时暂停它,你可以查看变量的值,执行自定的指令,并且按照你所认为合适的步骤来操作程序的进展。我们可以简单的理解成它是一个调试器
如下图,调试过程中程序运行到断点处时,控制台的右边会出 现“lldb”。
1 help
断点时,控制台输入”help“,回车,就能看到lldb命令及介绍
2 print
打印 常用p 代替
3 prin object
打印对象的详细信息 常用 po 代替
4 expression
可以在当前位置改变程序实际参数的值(不用重新运行项目) 使用过程中我们可以直接用 expr来代替expression。
5 call
一般调用没有返回值的方法时使用call 例如 在viewDidLoad中断点,然后输入
继续运行,view的背景就变成红色呢,很方便
6 image
mage list 查看工程中使用的库
image lookup –address 0x000000010e0979ac 程序崩溃的时候定位,查看具体报错位置 (不太好用)
断点里面根据作用和功能也有很多种类:普通断点、条件断点、异常断点、符号断点等
打上断点之后,对断点进行编辑,设置相应过滤条件。单击右键会弹出选项框:
|
|
Edit BreakPoint。这里面设置断点的筛选条件(双击断点也可以快速进入编辑断点的对话框)。
|
|
同样的,全局断点也是可以编辑的,单击右键或者双击断点就会弹出编辑框,编辑的项目和上述是一样的。
我们调试程序的大部分时候都是为了监控某个变量的变化,在代码中变量出现的地方添加断点不仅累而且还可能漏掉,事后还得一个一个删掉,实在很累。
我们可以通过为变量添加监控断点来简单地做到这一点。找到变量第一次出现的地方,添加一个普通断点,进入 debug 模式后在 Variables View 中右键变量,选择 Watch 变量名。这样,每一次该变量被改变都会触发断点告知我们
借助于OS X,因为iOS框架中没有创建Bundle的模板
删除工程中多余的文件
由于删除了plist文件,所以必须在Build Settings中删除对它的引用
我们创建项目时选的是OS X中的框架,所以此处需要修改Base SDK为iOS
设置完后,可以把我们需要的资源拷贝过来
按快捷键”Command + B”编译,编译成功后,Products下面的PictureOCRImage.bundle由红色变成了黑色,表示编译成功
可以通过右击 Show in Finder -> 查看文件夹 ->右击显示包内容,查看里面的内容
新建项目,点击iOS—Framework&—Cocoa Touch Static Library。
系统自动生成以工程名命名的.h和.m文件,可自定义的在目录下添加或删除文件,注意目录下Products文件夹有一个.a文件为红色,说明文件并不存在。这里我们将系统生成的.h和.m文件删除。
将需要打包的文件添加到工程中
点击Build Phases—Copy Files,左下角点击+号按钮,添加你需要暴露的接口头文件。
点击左上角,选择Edit Scheme,Build Configuration下选择Release,先注意检查下面Release是否为NO:Yes表示只编译选中模拟器设备对应的架构,No则为编译所有模拟器设备支持的cup架构(Debug版本同理),选择NO,然后分别在模拟器和真机下Command+B编译一下,会看到Products文件夹下的.a文件变为黑色,这个.a文件就是我们想要得到的静态库。
可以通过右击 Show in Finder -> 查看文件夹,打开Build—Products文件夹,会看到Release-iphoneos和Release-iphonesimulator文件夹,分别是真机和模拟器的.a文件,为了使用方便我们将两个版本的.a文件合并。
合并真机和模拟器.a文件,在终端输入以下命令行:lipo -create 模拟器.a文件的路径 真机.a文件的路径 -output 合并后的保存路径(例:lipo -create /Users/dcd/Library/Developer/Xcode/DerivedData/TestCocoa_Touch-gjlcdmvelybrhdfbqwxscxvvtsqy/Build/Products/Debug-iphonesimulator/libTestCocoa\ Touch.a /Users/dcd/Library/Developer/Xcode/DerivedData/TestCocoa_Touch-gjlcdmvelybrhdfbqwxscxvvtsqy/Build/Products/Release-iphonesimulator/libTestCocoa\ Touch.a -output /Users/dcd/Desktop/TestCocoaTouch.a)最终会在桌面得到一个合并后的TestCocoaTouch.a文件,再将暴露出来的.h头文件一起复制出来
查看静态库.a对处理器架构的支持,先cd到.a文件的路径下,命令行输入:
lipo -info xxxxx.a
至此,.a库的制作完成了
参考文章:
http://www.cnblogs.com/richard-youth/p/4856841.html
http://www.fx114.net/qa-173-100870.aspx
王巍 目前在日本横滨任职于LINE。工作内容主要进行Unity3D开发,8小时之外经常进行iOS/Mac开发。他的陈列柜中已有多款应用,其中番茄工作法工具非常棒。
池建强 70后程序员,Blogger。98年毕业,先后就职于洪恩软件、RocketSofeware和用友软件工程公司(后更名为瑞友科技),现任瑞友科技IT应用研究院副院长。该博客最初每天发送一条Mac技巧,不过目前已经形成了一种技术和人文结合的风格,时而随笔,时而技术。
许小帅 目前任职于腾讯,他还是iOS Feed站点的负责人。博客中对支付宝钱包插件分析的文章,引起了支付宝开发团队的反思,大家可以去感受一下。
萧宸宇 目前任职于Sumi Interactive。他出生于西南一个边陲小镇,梦想着能走出家乡,看看祖国的大好河山。他拥有丰富的 iOS 开发经验,在他的博客中发表了许多 iOS 开发的文章。
乔学士 目前任职于创业公司拓词,他正在为成为“代码手工艺人”而努力,目前主要进行 iOS 开发。最近他做得非常精彩的一件事就是汇总了WWDC 2013 视频英文字幕下载,大家可以前往他的博客感受一下。
破船 在移动领域摸爬滚打多年,经历了Symbian、Windows Mobile、Windows Phone和 iOS 等的洗礼。他希望能充分利用业余时间,努力做一名伪步道师。
骆仕恺 骆仕恺目前任职于爱折客 / 爱美味。他主要从事 iOS 和Java开发。拥有丰富的 iOS 开发经验。
耿健桓 目前在清华大学攻读硕士,之前他是著名站点知乎的 iOS 技术主管。
李择一 目前任职于百度,他在移动开发领域积累了丰富的经验,特别是 iOS 开发。最近他在博客中发表关于OC中block的追究非常有深度,大家一定要去感受一下。
王轲 是一名iOS独立开发者,也是IndieBros Studio创始人。在博客中有几篇关于 iOS 性能优化的文章,值得大家去看看。
连栩 目前工作在加拿大温哥华,运营着自己的公司 Beyondcow,他是一名 OSX、iOS 程序员同时也是用户体验,交互界面设计师。在开发的软件 Miao 是一款全功能 Mac 微博客户端。他的博客中有许多Cocoa开发的文章,值得大家去品读。
高嘉峻 目前任职于天猫。是一名资深80后,号称会写代码的猪。最近他的博客中出现了两篇关于iOS中单元测试的文章,非常棒。
webfrogs 目前在北京当一枚程序猿,有幸成为80后的尾巴。目前行走在iOS开发的道路上,用博客记载一路上的所见所闻所想。
景总 目前任职于天猫国际移动开发CTO,曾就职于平安科技项目经理,对IM和APP性能优化有深刻间接。
谭灏 目前就职于腾讯mac管家部门,他是一名 OSX、iOS 程序员,他的博客中有许多mac,ios开发的文章,值得大家去品读。
田伟宇 目前任职于天猫,全能人才。最近他的博客中出现了篇关于iOS应用架构详谈的文章,非常棒。
Markdown语法:
|
|
预览效果:
Markdown语法:
|
|
预览效果:
粗体
粗体
斜体
斜体
Markdown语法:
|
|
预览效果:
文字删除线
Markdown语法:
|
|
预览效果:
Markdown语法:
|
|
预览效果:
Markdown语法:
|
|
预览效果:
ABCD | EFGH | IGKL |
---|---|---|
a | b | c |
d | e | f |
g | h | i |
Markdown语法:
|
|
预览效果:
引用的文字引用的文字引用的文字引用的文字引用的文字
引言内的引言引言内的引言引言内的引言
引用的文字引用的文字引用的文字引用的文字引用的文字
Markdown语法:
|
|
预览效果:
A^A
Markdown语法:
|
|
预览效果:
Markdown语法:
|
|
预览效果:
\
`
*
_
{}
[]
()
#
+
-
.
!
> hello world
预览效果
hellow world
> hello world
> hello world
> hello world
或者
> hello world
hello world
hello world
预览效果
hello world
hello world
hello worldhello world
hello world
hello world多层嵌套
aaaaaaaaa
bbbbbbbbb
cccccccccc
预览效果
aaaaaaaaa
bbbbbbbbb
cccccccccc
标记之外`hello world`标记之外
标记之外hello world
标记之外
12345
<div> <div></div> <div></div> <div></div></div>
|
|
自定义语法
1234
var num = 0;for (var i = 0; i < 5; i++) { num+=i; }
|
|
注:兼容性一般 要隔开一行
这是文字……
- [x] 选项一
- [ ] 选项二
- [ ] [选项3]
为什么写博客
写博客对自己的提升是很大的,可能写一篇体现不出来,但是只要你坚持写效果就很明显,好处人个认为有以下几点