crashlytics 手机qq怎么退出出

51CTO旗下网站
如何手动解析CrashLog
解决崩溃问题是移动应用开发者最日常的工作之一。如果是开发过程中遇到的崩溃,可以根据重现步骤调试,但线上版本就无能为力了。好在目前已经有很多不错的第三方CrashLog搜集平台(如友盟、Crashlytics等)为我们做好了解析工作,甚至在Xcode7里苹果也跟进了解析线上版本崩溃日志的功能,为开发者减轻了不少负担
作者:来源:王中周的技术博客| 09:52
解决崩溃问题是移动应用开发者最日常的工作之一。如果是开发过程中遇到的崩溃,可以根据重现步骤调试,但线上版本就无能为力了。好在目前已经有很多不错的第三方CrashLog搜集平台(如友盟、Crashlytics等)为我们做好了解析工作,甚至在Xcode7里苹果也跟进了解析线上版本崩溃日志的功能,为开发者减轻了不少负担。尽管通常已经不需要我们手工处理CrashLog,了解CrashLog的还原原理和方法还是有必要的。
.dSYM(debugging SYMbols)又称为调试符号表,是苹果为了方便调试和定位问题而使用的一种调试方案,本质上使用的是起源于贝尔实验室的DWARF(Debugging With Attributed Record Formats),其在.xcarchive目录中的层次结构为:
.xcarchive&--dSYMs&|--Your.app.dSYM&|--Contents&|--Resources&|--DWARF&
关于DWARF的具体内容以后有机会再说。我们能解析CrashLog全靠.dSYM文件,解析方式见后文。
二、确定符号表和崩溃日志的一致性
有了符号表文件,有了崩溃日志文件,在解析之前一定要确保二者的对应关系,否则就算按照下述步骤解析出内容也肯定是不准确的。二者的对应关系可以通过UUID来确定。
1、从崩溃日志中获取UUID
崩溃日志比较靠下的位置有个Binary Images模块,其第一行内容如下:
Binary&Images:&0xa2000&-&0x541fff&Your&armv7&/var/mobile/Containers/Bundle/Application/645D-4161-924B-BDE170FA64CC/Your.app/Your&
从中可以看到关于你应用的若干信息:
代码段的起终地址为:0xa2000 & 0x541fff
运行你应用的CPU指令集为:armv7
应用的UUID为:a5c8d3cfda0bf3a0ac64(不区分大小写)
2、从符号表中获取UUID
执行以下命令从符号表中提取UUID:
dwarfdump&--uuid&Your.app.dSYM&
dwarfdump&--uuid&Your.app.dSYM/Contents/Resources/DWARF/Your&
执行结果为:
UUID:&A5C8D3CF-DA65-3966-89E4-370BF3A0AC64&(armv7)&Your.app.dSYM/Contents/Resources/DWARF/Your&
由此得到armv7指令集的UUID为:A5C8D3CF-DA65--370BF3A0AC64(如果你的二进制文件支持多个指令集,这里会列出每个指令集对应符号表的UUID),通过和崩溃日志中的对比发现二者一致,才可进行进一步的解析操作。
三、计算崩溃符号表地址
以下面的崩溃堆栈为例:
Thread&0:&0&libobjc.A.dylib&0x33f10f60&0x33efe000&+&77664&1&Foundation&0x273526ac&0x&+&34476&2&Foundation&0x27355c3e&0x&+&48190&3&UIKit&0x29ef9d1c&0x29bbc000&+&3398940&4&UIKit&0x29ef9c9a&0x29bbc000&+&3398810&5&UIKit&0x29ef954c&0x29bbc000&+&3396940&6&UIKit&0x29c3a16a&0x29bbc000&+&516458&7&UIKit&0x29e4b8e6&0x29bbc000&+&2685158&8&UIKit&0x29c3a128&0x29bbc000&+&516392&9&Your&0x000f0846&0xa2000&+&321606&10&UIKit&0x29e90fb2&0x29bbc000&+&2969522&11&UIKit&0x29e91076&0x29bbc000&+&2969718&12&UIKit&0x29e867cc&0x29bbc000&+&2926540&13&UIKit&0x29c9e8ea&0x29bbc000&+&927978&14&UIKit&0x29bc8a6a&0x29bbc000&+&51818&15&QuartzCore&0x295f0a08&0x295e4000&+&51720&16&QuartzCore&0x295ec3e0&0x295e4000&+&33760&17&QuartzCore&0x295ec268&0x295e4000&+&33384&18&QuartzCore&0x295ebc4c&0x295e4000&+&31820&19&QuartzCore&0x295eba50&0x295e4000&+&31312&20&QuartzCore&0x295e5928&0x295e4000&+&6440&21&CoreFoundation&0x266d0d92&0x&+&839058&22&CoreFoundation&0x266ce44e&0x&+&828494&23&CoreFoundation&0x266ce856&0x&+&829526&24&CoreFoundation&0x2661c3bc&0x&+&99260&25&CoreFoundation&0x2661c1ce&0x&+&98766&26&GraphicsServices&0x2da1a0a4&0x2da11000&+&37028&27&UIKit&0x29c2a7ac&0x29bbc000&+&452524&28&Your&0x0024643a&0xa2000&+&1721402&29&libdyld.dylib&0x34484aac&0x&+&6828&
1、 符号表堆栈地址计算方式
要想利用符号表解析出崩溃对应位置,需要计算出符号表中对应的崩溃堆栈地址。而从上述堆栈中第9行可以看到,应用崩溃发生在运行时地址0x000f0846,该进程的运行时起始地址是0xa2000,崩溃处距离进程起始地址的偏移量为十进制的321606(对应十六进制为0x4E846)。三者对应关系:
0x000f0846&=&0xa2000&+&0x4E846&
对应的公式为:
运行时堆栈地址 = 运行时起始地址 + 偏移量
崩溃堆栈中的起始地址和崩溃地址均为运行时地址,根据虚拟内存偏移量不变原理,只要提供了符号表TEXT段的起始地址,再加上偏移量(这里为0x4E846)就能得到符号表中的堆栈地址,即:
符号表堆栈地址 = 符号表起始地址 + 偏移量
2、获取符号表中的TEXT段起始地址
符号表中TEXT段的起始地址可以通过以下命令获得:
$otool&-l&Your.app.dSYM/Contents/Resources/DWARF/Your&
运行结果中的片段如下:
Load&command&3&cmd&LC_SEGMENT&cmdsize&736&segname&__TEXT&vmaddr&0x&vmsize&0x&fileoff&0&filesize&0&maxprot&0x&initprot&0x&nsects&10&flags&0x0&
其中的vmaddr 0x字段即为TEXT段的起始地址。
3、计算符号表地址
符号表堆栈地址 = 符号表起始地址 + 偏移量
0x52846&=&0x4E846&+&0x4000&
即符号表中的崩溃地址为0x52846,接下来就可以根据这个地址解析出崩溃位置了。
四、崩溃信息还原
有了符号表的崩溃地址,有以下几种方式解析崩溃信息:
1、dwarfdump
命令如下:
$dwarfdump&--arch&armv7&Your.app.dSYM&--lookup&0x52846&|&grep&'Line&table'&
需要注意的是:
这里的armv7是运行设备的CPU指令集,而不是二进制文件的指令集
比如armv7指令集的二进制文件运行在arm64指令集的设备上,这个地方应该写arm64。
&lookup后面跟的一定是经过准确计算的符号表中的崩溃地址
使用dwarfdump解析的结果较杂乱,因此使用grep命令抓取其中关键点展示出来
运行结果如下:
Line&table&dir&:&'/data/.../Src/OBDConnectSetting/Controller'&Line&table&file:&'OBDFirstConnectViewController.m'&line&882,&column&5&with&start&address&0x768&
其中第一行是编译时文件目录,第二行包含了崩溃发生的文件名称以及文件中具体行号等信息,有了这些信息就能准确定位崩溃原因啦。
atos是另一种更加简洁的崩溃日志解析方法,使用方式如下:
$atos&-o&LuBao&-arch&armv7&0x52846&
其执行结果如下:
-[OBDFirstConnectViewController&showOilPricePickerView]&(in&Your)&(OBDFirstConnectViewController.m:882)&
相对dwarfdump命令的解析结果,更加简洁直观的指出了崩溃发生的位置。
3、无需符号表崩溃地址的解析方式
实际上,atos还提供了另外一种无需计算崩溃地址对应的符号表地址的方式,命令格式如下:
$atos&-o&Your.app.dSYM/Contents/Resources/DWARF/Your&-arch&armv7&-l&0xa2000&0x000f0846&
其中-l选项指定了二进制文件在运行时的起始地址0xa2000(获取方式见Binary Images相关内容),后面跟的是崩溃发生的运行时地址0x000f0846,解析结果和使用计算得到的符号表中崩溃地址一致:
-[OBDFirstConnectViewController&showOilPricePickerView]&(in&Your)&(OBDFirstConnectViewController.m:882)&
【责任编辑: TEL:(010)】
大家都在看猜你喜欢
头条头条关注热点头条
24H热文一周话题本月最赞
讲师:95676人学习过
讲师:30722人学习过
讲师:206640人学习过
精选博文论坛热帖下载排行
本书共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计模式。前言先介绍这本书的用法;第1章到第11章陆续介绍的设...
订阅51CTO邮刊您正在使用IE低版浏览器,为了您的雷锋网账号安全和更好的产品体验,强烈建议使用更快更安全的浏览器
发私信给张海春
导语:你的iOS应用程序为啥会崩溃?你花费时间开发,然后在模拟器上面运行,还要费心费力去测试,然后好不容易进入App Store,结果用户却开始抱怨。创业公司Crashlytics采用了一个方法让你找到应用崩溃的原因以及如何解决这些问题。Crashlytics是基于云的错误报告解决方案,旨在找到所
同步到新浪微博
关注最前沿的技术、产品以及最拉风的公司,扯八卦的请滚开,,有事请Gtalk,没事请QQ,微信ID“benimaru”;#不要害怕自己与众不同,人们总想把你拉回平庸#
当月热门文章
为了您的账户安全,请
您的邮箱还未验证,完成可获20积分哟!
您的账号已经绑定,现在您可以以方便用邮箱登录
请填写申请人资料fhbystudy的专栏
https://blog.csdn.net/
https://static-blog.csdn.net/images/logo.gif
https://blog.csdn.net/fhbystudy
https://blog.csdn.net/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
该话语出自孔子,太随心并不是很好,太所欲也并不妙。凡事适可而止,不可过量自由是一种境界,一种随心所欲的境界。
心有多宽,自由就有多大。但是自由也是建立在大家都遵守规矩的基础上的,任何一个组织都是这样。不要把个人的自由建立在组织中别人的不自由之上。如果是那样,经过恶性循环,组织必然走向灭亡。组织的一个重大工作就是建立组织的规矩,让组织中的人都相对自由的规矩,这里的相对是说遵守规矩就自由的意思。小的来说,一个由开会参与者组成的临时组织,最重要的规矩就是守时,不要迟到。一个人的迟到,耽误的是组织中其他所有人的时间。如果是十个人的会议,一分钟的迟到就变成了十分钟的不自由;十分钟的迟到就变成了一百分钟的不自由。第一次一个人迟到,第二次可能就是两个,后面越来越多,会议自然就开不成了,组织就解散。组织就需要建立规则,守时以及对不守时者的惩罚是第一位的。大的来说,一个国家建立法律就是在建立组织的规矩。每个人都遵守法律大家才会自由。要不然一个人的自由可能就是另一个人的生命。那样的国家必然灭亡。无论是做事还是交友,我们往往会碰到一个怪圈。不做事或者做事做得太慢,往往会被人视为不够进取;而如果做事做得太激进,要么是让人感觉火候未到,要么是被人嫉妒,遭到枪打出头鸟的命运。
交友也是如此,如果太过一本正经,往往会让人觉得你不好接近,如果太过亲近,则又让感觉你太随意。有时的确很矛盾。 近期又温习了孔老夫子的论语,终于找到了答案,人生的终极目标大概是“至广大而尽精微,极高明而道中庸”,唯有如此方可达到“随心所欲而不逾矩”,到了这样的境界,大概可以识大局、识大体,中而不偏、游刃有余。根据我们孔老夫子的判断,要达到这个境界,至少也要达到七旬的年龄,看来我的这种感觉也属正常,毕竟是一介凡夫俗子。
理论是弄清楚了,但问题并没有解决,我们不能等到七十岁才去做事、交友,看来我们是不可避免要得罪人、做错事了,这也许就是“人生十有八九不如意”的深刻内涵吧。 话虽这么讲,但我们总还是希望尽量少犯错、尽量少得罪人,孔老夫子真是伟大,他给我们得出了答案、“指明了方向”,那就是我们要努力做君子,什么样的人是君子呢,他又进一步给出了一个标准,叫“仁者不忧、智者不惑、勇者惧”,精辟、精辟。但凡我们做事出错,要么是不够仁,私心太重;要么是不够智,做事漏洞百出而不自知;要么是不敢勇,患得患失。
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
本文是原创翻译,转载请看清文末的转载要求,谢谢合作!
我之所以要用聪明和懒惰来形容高效的程序员,原因有以下几点:
聪明是因为能找出问题的正解懒惰是因为不愿写多余的代码(即不会长时间地坐在电脑前)
好的软件开发过程应该是懒惰的软件开发,亦称耐心开发,原因是开发人员在写代码之前会先将时间花在透彻地考虑各种解决方案上。
这是懒惰开发的主旨,即在不了解之前就不会开始写代码。这个负责任的态度减少了他们所需要写的代码量。
主要需要做到以下几点:
真正了解需求、获得产品管理,关键是要明确什么才是实际要求确保与团队中其他开发人员之间的顺利对接这些过程都是需要时间的。然而,不少开发人员往往一拿到问题就开始急于敲代码,然后再花费大把大把的时间修改代码。
事实上,只有5%都不到的时间才真正是有生产力的。所以下回你要是看到某个开发人员将他100%的时间都紧盯着屏幕,不与其他人做任何互动,那么只能说你找来的程序员可不太好啊。
这是一个不好的迹象——如果开发人员总是写代码的话
高效的开发人员会时不时地检查对需求的理解,以确保与代码相一致。富有生产力的开发人员会经常与产品经理/业务分析员分析交流,也时常可以看到他们与同事和架构师出双入对。当然资深的开发人员也会因其自身丰富的经验而变得非常有效率。事实上,最好的开发人员与其他人相比:
编写初始编码所用的时间约为1比20调试时间的比例超过1比25程序执行速度大概是1比10程序大小为1比5然而,总的说来,随着时间的推移,开发人员的生产效率并没有随之而增长,即从成千上万的开发人员那儿可以证实,丰富的经验和生产力是无关的。事实上,我们的生产效率在过去50年间已经提高了8倍,所以,从总体上说,经验与生产力水平是不相干的。
为什么在代码上的懒惰会变得如此重要?
很多人很勤快,还没有好好理解需求就开始动手写代码。并且,迅速编写的代码往往不能很好的适应其他人的代码。而这一问题往往只有在集成代码的时候才能被发现,但是已经晚了。优秀的开发人员很有耐心,他知道快速编码的风险。
开发人员在心理上很重视他们的代码
差的开发人员往往不愿意去改变自己写得不好的代码。他们只会选择增添更多的代码来弥补不足,而不会重写不理想的代码。更糟的是,他们往往会去责怪其他人有不良的代码,而不愿意自我检讨。在他们手中,最终只会导致一个严重缺陷和不稳定的系统。
当然,好的开发人员也会写出差的代码,非最优的代码。所不同的是,他们愿意认识自己的不足,并且对于有问题的部分:
如果代码大致还是能正确运行的,那就重构代码否则,就重写代码当开发人员生产并维护了非最优代码,随着时间的推移,那么修改这些代码就会变得越来越难。这是因为你的同事需要在这些代码接口上写代码,然后再创建繁杂的接口等等使用这些非最优代码。并且随着代码库的发展,会有更多更多的代码是依赖这些原始代码编写出来的。故而,后面写的代码再怎么好也几乎增加不了代码的稳定性,也不能对略有改动时bug增殖的情况产生影响。总之,开发过程会变得越来越慢、越来越难。
所以,如果有什么疑问,那么不妨偷个懒,晚点再写代码,须知,磨刀不误砍柴工啊!
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
类和结构体
Swfit并不要求你为自定义类和结构去创建独立的接口和实现文件,只需要在一个文件定义一个类或结构体,Swift自动生成外部接口
通常类的实例被称为对象,Swift中称为实例而不是对象
通过class和struct来声明结构体,大括号里定义它们的内容
2.类和结构体的实例
使用初始化方法来生成新的实例
3.访问属性
使用点语法访问类和结构体的属性,或者子属性
4.结构体类型的初始化方法
所有结构体都有一个自动生成的初始化方法,用于初始化结构体中成员的属性
5.结构体和枚举都是值类型的
值类型在赋值给变量、常量或者传入到一个函数的时候,操作的都是值的拷贝,意味着它们的实例以及实例所包含的属性在代码传值的时候都会被复制
枚举也是一样的
6.类是引用类型
与值类型不同,引用类型赋予到一个变量或常量之后,操作的不是拷贝,而是实例本身
以下tenEighty和alsoTenEighty虽然是常量,但依然可以修改它们的属性,因为这两个常量本身不会被改变,仅仅存了ViewMode()实例的引用
7.恒等运算符
因为类是引用类型,可能出现多个变量在引用同一个类实例的情况,所以Swift提供了两个恒等运算符,判断两个常量或者变量是否引用同一个类实例
&等价于( === ) 表示两个类类型的常量或变量引用同一个实例
&不等价( !== )
8.类和结构体的选择
值传递
主要用来封装少量相关简单的数据值
构建一个实例在赋值或传递时,要明确封装的数据是被拷贝而不是引用
在结构体中存储的值类型属性,都是被拷贝而不是引用
结构体不需去继承另一个已存在在类型的属性或行为
在实际应用中,绝大部分的自定义数据都应该是类,而非结构体
9.集合类型的赋值和拷贝行为
Swift中的数组和字典都是结构体,当数组被赋值给一个常量或变量,或传递到函数或方法时,和字典的拷贝都与其他结构体有不同
字典类型的赋值和拷贝
如果字典中存储的key/value是值类型,在赋值或调用时,都会被拷贝,如果是引用类型,将会被引用
数组的赋值和拷贝
数组的赋值和拷贝要比字典复杂,当操作数组时能提供接近C语言的功能,并且拷贝行为只在必要时才发生,将数组赋值给其他变量时,数组的内容不会被拷贝,而是被共享出来公用一个,当数组元素被修改,在另一组数组也显示同样的结果,而拷贝行为只在修改了数组长度的时候发生,发生拷贝的情况与字典类似
10.确保数组的唯一性
操作数组的时候有必要确认这个数组是有一个唯一拷贝的,调用unshare方法确定数组引用的唯一性,如果一个数组被多个变量引用,在其中的一个变量调用unshare方法则会拷贝此数组,此时这个变量会有属于他自己的独立数组拷贝。
11.判断两个数组是否共用相同元素
或者判断两个数组是否共用相同的元素
12.强制复制数组
通过调用copy方法进行强制复制,这方法对数组进行浅拷贝,并返回一个包含此拷贝的新数组,
unshare方法仅会在确认有必要的时候菜户创建数组拷贝,copy方法在任何时候都会创建新的拷贝,即使引用已经是唯一的
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
class ZWDBManager: NSObject {
//前提将FMDBDatabase的头文件加入到桥接文件中
var dataBase:FMDatabase?
var lock:NSLock?
//创建单例
class func shareInstance()-&ZWDBManager{
struct qzSingle{
static var predicate:dispatch_once_t = 0;
static var instance:ZWDBManager? = nil
//保证单例只创建一次
dispatch_once(&qzSingle.predicate,{
qzSingle.instance = ZWDBManager()
return qzSingle.instance!
//构造方法中对数据库进行创建并打开
var path:String = NSHomeDirectory().stringByAppendingString("/Documents/MCA.db")
lock = NSLock()
dataBase = FMDatabase(path:path)
if dataBase!.open(){
var createImgTableSql:String = "create table if not exists ImgInfo(Id integer primary key autoincrement,picName varchar(256),picPath varchar(256),FID varchar(256),userID varchar(256))"
//在这里要传入两个参数:第一个为创建表的sql,第二个为多参数(个人理解,之前只传入sql但是一直报错,个人理解Swift中可返回多参数,传入多参数可能也如此)
var isSuccessed:Bool = dataBase!.executeUpdate(createImgTableSql,withArgumentsInArray: [])
if isSuccessed {
println("成功!")
println(dataBase!.lastErrorMessage())
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
阅读:2490
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
如果我们要检测app版本的更新,那么我们必须获取当前运行app版本的版本信息和appstore 上发布的最新版本的信息。
当前运行版本信息可以通过info.plist文件中的bundle version中获取:
这样就获取到当前运行的app的版本了
要获取当前app store上的最新的版本,有两种方法,
一、在某特定的服务器上,发布和存储app最新的版本信息,需要的时候向该服务器请求查询。
二、从app store上查询,可以获取到app的作者,连接,版本等。
具体步骤如下:1,用 POST 方式发送请求:你的应用程序名称&entity=software
更加精准的做法是根据 app 的 id 来查找:你的应用程序的ID
#define APP_URL 你的应用程序的ID
你的应用程序的ID 是 itunes connect里的 Apple ID
2,从获得的 response 数据中解析需要的数据。因为从 appstore 查询得到的信息是 JSON 格式的,所以需要经过解析。解析之后得到的原始数据就是如下这个样子的:
resultCount = 1;
artistId = 开发者 ID;
artistName = 开发者名称;
price = 0;
isGameCenterEnabled = 0;
languageCodesISO2A =
trackCensoredName = 审查名称;
trackContentRating = 评级;
trackId = 应用程序 ID;
trackName = 应用程序名称";
trackViewUrl = 应用程序介绍网址;
userRatingCount = 用户评级;
userRatingCountForCurrentVersion = 1;
version = 版本号;
wrapperType =
然后从中取得 results 数组即可,具体代码如下所示:
NSDictionary *jsonData = [dataPayload JSONValue];
NSArray *infoArray = [jsonData objectForKey:@"results"];
NSDictionary *releaseInfo = [infoArray objectAtIndex:0];
NSString *latestVersion = [releaseInfo objectForKey:@"version"];
NSString *trackViewUrl = [releaseInfo objectForKey:@"trackViewUrl"];
如果你拷贝 trackViewUrl 的实际地址,然后在浏览器中打开,就会打开你的应用程序在 appstore 中的介绍页面。当然我们也可以在代码中调用 safari 来打开它。
UIApplication *application = [UIApplication sharedApplication];
[application openURL:[NSURL URLWithString:trackViewUrl]];
参考博客:http://hi.baidu.com/yanh105/item/7378a98ffca6a8804414cfa0
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
Xcode IDE拥有着众多非常高大上的工具,而予以辅助的插件更是在Xcode的基础上对相关功能进行改进与扩展。本文总结介绍了备受开发者喜爱的10款开源Xcode插件,涉及代码编辑、注释、管理等各个方面。
Xcode IDE拥有着诸如导航、重构、校准等众多非常高大上的工具,而予以辅助的插件更是在Xcode的基础上对相关功能进行改进与扩展。在应用开发过程中,通过开源包管理器对插件进行安装管理,打造最为强大的开发环境,早已成为开发者们的必备功课。本文总结介绍了备受开发者喜爱的10款开源Xcode插件,涉及代码编辑、注释、管理等各个方面。
说到Xcode那些炙手可热的插件,很多开发者首先都会想到Code Pilot。Code Pilot是Xcode 5的一款扩充插件,能够帮助开发者无需鼠标操作,即可在项目中快速方便地查找文件、方法和符号。
Code Pilot由Macoscope公司开发,其CEO为。Code Pilot基于Apache 2.0许可协议开源,采用模糊查询来匹配,计算结果依照其相关性进行排序,而开发者只需轻点几下键盘就可以跳转到自己所寻找的方法。
相关链接:、
XcodeBoost是一款可以让开发者轻而易举地检查和修改Objective-C代码的插件。XcodeBoost能够自动进行一些繁琐的操作,比如方法的定义与声明、添加基于命令行的代码处理(剪切/复制/粘贴/重复/删除行)、持续高亮等。
相关链接:XcodeBoost的
ClangFormat-Xcode是一款格式化代码工具,能够让开发者使用Clang将代码格式化为LLVM、Google、Chromium、Mozilla或WebKit等格式,其开发者为来自37signals的Travis Jeffery。通过ClangFormat,开发者不仅可以实现对代码的自动或批量格式化,还可以进行自定义配置。
相关链接:ClangFormat-Xcode的
XAlign是一款专门用于代码整理的Xcode插件,其作者为来自Geek Zoo Studio的开发者QFish。XAlign能够对开发者的代码非常快速地进行对齐优化,有“=”、宏定义、属性三种对齐模式。当然,如果默认的对齐风格不是自己喜欢的,开发者还可以自定义或提出issues。
相关链接:XAlign的
KSImageNamed是一款能够帮助开发者在Xcode中自动补全图片文件名称的插件,其开发者来自美国波士顿。KSImageNamed支持NSImage和UIImage,当开发者写到“[UIImage imaged:”时,便会自动将项目中的图片名称提示出来。此外,KSImageNamed还带有预览功能,对于经常使用代码生成图片的开发者可谓是十分便利。
相关链接:KSImageNamed的
Fuzzy Autocomplete是一款Xcode 5代码自动补全插件,支持Xcode 5.0、5.1,兼容KSImageNamed,其开发者为来自澳大利亚墨尔本的Jack Chen(创始人)和波兰华沙的Leszek ?la?yński(Fuzzy Autocomplete v2.0作者)。
Fuzzy Autocomplete通过添加模糊匹配来提高Xcode代码自动补全功能,开发者无需遵循从头匹配原则,只要记得方法中的某个关键词即可进行匹配,极大地提高了工作效率。
相关链接:Fuzzy Autocomplete的
BBUDebuggerTuckAway是一款支持自动隐藏Debugger的Xcode插件,其开发者为来自德国柏林Contentful GmbH公司的。使用BBUDebuggerTuckAway,开发者能够实现在编辑代码时,自动隐藏底部的调试栏。
相关链接:BBUDebuggerTuckAway的
Dash Xcode plugin是Bogdan Popescu开发的一款集成了Dash文档查看器应用的Xcode插件,允许开发者在使用Option-Click或作用相同的快捷键操作查看当前文本的相关文档时,用Dash代替Xcode的文档查看器。
相关链接:Dash-Plugin-for-Xcode的
兼容Xcode 5.1的HOStringSense可以说是大段文本利器,对于开发者而言,在输入大段文本时,如果文本中包含了各种换行和特殊字符,那是相当地头疼,但通过HOStringSense,一切的问题都将迎刃而解。
HOStringSense由来自德国的Mac和iOS开发者Dirk Holtwick开发,完美支持编辑正则表达式、多行文本、内联HTML等,还提供了极为快速的字符串长度统计反馈。
相关链接:HOStringSense-for-Xcode的
最后,再来介绍一款颇受大神喜爱的,出自国内iOS开发者之手的注释辅助插件——XToDo。这款由UniT微博客户端作者TraWor所开发的插件,可以将项目代码中的TODO、FIXME等注释进行收集并列举出来。
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
阅读:1006
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
:OS X上非常出色的包管理工具。
:分布式版本控制系统和源码管理系统,其优点是:快和简单易用。对于新手来说,可在此查看。
:声望日盛的资源分享之地。
:GitHub的替代选择。
:一个设计的非常美观的git客户端,不能取代你从命令行获得的所有功能,但使用起来非常简单。
:Windows和Mac上免费的Git和Mercurial客户端。如果使用BitBucket托管项目,而你恰恰不喜欢命令行,或者仅仅想要一个涵盖大部分git功能的GUI,那么SourceTree是个很好的选择。
Kaleidoscope ($):一款强大的快速的文件、图片或者文件夹比较工具,界面美观。这款应用的价格有点小贵,所以如果你仅仅是想查看文件的不同,推荐你使用Github或者SourceTree。
命令行工具
:第三方库的管理利器,允许你简单地把第三方库整合进自己的应用中。对我个人来说,我基本上每个项目都使用CocoaPods。(相关阅读:吐槽)
:开源的Xcode 5包管理器,可以让你发现和安装插件、模板以及配色方案,无需手动复制文件。现已支持Xcode 5。
:一个git扩展集合,以图形化客户端来管理资料库。
:优秀的iOS开发命令行工具集。具体功能可在其官方网站查看。
Xcode增强工具
:一款针对Mac和iOS平台的日志框架,快速、简单、功能强大,并具有极好的灵活性。开发者可用它为Debug和版本发布指定不同级别的日志,可通过CocoaPods进行安装。
:为应用调试输出添加有颜色的代码,从而简化调试,可与CocoaLumberjack一同使用。
:一款Xcode插件,能让开发者更简单更视觉换地使用UIColor和NSColor。当光标位于UIColor实例上时,颜色将会出现在屏幕上。点击颜色则可打开颜色选择器。该插件的编辑菜单增加了一些项目来插入颜色或者暂时禁用颜色高亮。菜单没有默认的键盘快捷键,但你可以通过系统的键盘参数选择进行设置。
:一款Xcode插件,允许你直接从Xcode管理CocoaPod依赖。
($):一个浏览API文档的工具,快速并集成了大量不同的应用,比如Xcode、Alfred、Textmate以及Sublime Text等。最方便的是你可以下载文档集离线观看。
:一个添加了Dash支持的Xcode插件。当使用Option-Click(或者作用相同的键盘快捷键)查看选中符号的文档时,该工具允许你使用Dash代替Xcode自己的文档查看器。
:这视乎是最明显的一个,但却可以减少大量开发时间。创建起来像那么简单,这里有很多优秀的示例(、 )。
轻量级编辑器
:Mac OS X上一个可高度自定义的编辑器,尤其在我想做出一个快速改变但又不想等待Xcode加载的时候。该工具目前已经开源()。
($):Mac OS X上另一款非常受欢迎的轻量级,可高度自定义的编辑器。
:适用于iOS的TDD/BDD测试平台。Kiwi让单元测试更加易读,类似Ruby测试工具RSpec。(相关阅读:
:一个轻量级的Objective-C和Cocoa的TDD/BDD框架,类似Kiwi,有类似RSpec的语法。通常会结合一起使用。
:Kiwi和Specta都基于该框架,同样使用RSpec风格的语言。
上述Kiwi、Specta、Expecta以及Cedar都可以通过CocoaPods添加到你的项目中。
:xctool是来自Facebook的优秀开发工具,可以让你通过命令行创建和测试你的应用。除了比苹果提供的xcodebuild工具好用外,它的输出可以注入CI软件,因此更具灵活性。
:一个开源的持续集成服务器,配置简单。通过Xcode插件,你可以用Jenkins来测试、签名、创建以及分发应用。Jenkins非常人性化,ANSI彩色输出,这些都是额外的功能。
:该工具自动化了创建、分析以及测试应用的过程。使用了Mac OS X server和苹果的指令。
为了推广Bots,苹果于日向开发者免费发布了。
:让创建应用,把应用上传至FTP服务器变得异常简单的CLI工具。:知名的iOS应用测试平台。2014年3月,苹果收购了该平台的开发商Burstly。TestFlight表示将会停止开发 Android 产品,并且目前的 beta 测试 SDK 将不支持新顾客注册。目前的顾客仍然可以使用 TestFlight。
($) :类似TestFlight的服务,但也包含崩溃报告和用户反馈。
:适用于iOS和Android平台的知名应用分析工具,包括Walmart、Paypal、Square以及Path在内的多家公司都使用了Crashlytics的服务。
Crashlytics于2011年成立,Crashlytics软件可以帮助App开发者查找导致bug的原因,可以让开发者清楚知晓哪些代码导致了应用崩溃,从而让开发者更为容易地修复问题。Crashlytics还提供了dashboard工具(Web app和Mac app),让开发者更高效地发现和解决问题。Crashlytics于2013年1月被Twitter收购。
($):一个可查看所有HTTP和SSL/HTTPS流量的工具。这款工具对于你测试和服务器端进行交互的应用非常有用。
($):一款Mac OS X app,可用来创建、设计、编辑以及浏览SQLite 3数据库文件。
($):一个Mac OS X 上FTP客户端,有着非常漂亮的用户界面和有用的功能。
:OS X上一款Markdown的编辑器。非常适用于编写自述文件、变更日志以及其他方面的内容。
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
阅读:3076
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
我们写出来的代码会给很多人看,为了使代码清晰简洁,方便阅读理解,都会统一遵从一定的代码规范,Objective-C同样如此。
主要参考规范:
简单总结一下目前接触到的:
1.代码行度最大为100列(C++的是80)
2.声明类或方法时,注意空格的使用,参数过多时可换行保持对齐,
调用方法时也是如此,参数都写在一行或换行冒号对齐,
3.命名规则
类名首字母大写,方法首字母小写,方法中的参数首字母小写,同时尽量让方法的命名读起来像一句话,能够传达出方法的意思,同时取值方法前不要加前缀“get”
变量名小写字母开头
常量以小写字母k开头,后续首字母大写
4.关于注释
注释很重要,但除了开头的版权声明,尽可能把代码写的如同文档一样,让别人直接看代码就知道意思,写代码时别担心名字太长,相信Xcode的提示功能。
5.实例变量应该在实现文件.m中声明或以@property形式在.h文件中声明,一定要直接在.h文件声明,加上@priavte,另外,使用@private、@public,前面需要一个缩进空格。
6.尽可能保证 .h文件的简洁性,可以不公开的API就不要公开了,写在实现文件中即可。
7.Xcode支持Objective-C/C/C++混编,所以引用头文件时:#import Ojbective-C/Objective-C++头文件(Objective-C++是Objective-C与C++混编的文件),#include C/C++头文件。
8.写delegate的时候类型应该为weak弱引用,以避免循环引用,当delegate对象不存在后,我们写的delegate也就没有存在意义了自然是需要销毁的,weak与strong可以参考上一篇文章介绍。
9.实例变量声明时变量名前面加下划线“_”,局部变量不用加。
10.使用Block时,内容四个空格缩进,“^”后带有参数时,参数与“{”之间有一个空格缩进
11.建议使用“#pragma mark”,方便阅读代码
其他详细参考两个规范说明, 还列举了详细的命名要求。
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
ASIHTTPRequest (作者:BenCopsey) 是一个使用简单,可用于各种从简单到复杂的 HTTP 请求,或者可用于处理 Amazon S3、Rackspace 等REST 服务的强大框架。
不幸的是,Ben 早在 2011 年 9 月 21 日就已经声明停止开发和支持该框架(见http://allseeing-i.com/%5Brequest_release%5D; )。
Ben 推荐了许多可替代的框架(比如AFNetworking, RestKit 或 LRResty)。但最有潜力的莫过于Mugunth Kumar 的 MKNetworkKit 。Mugunth 曾发布了许多高质量的开源的 iOS/Mac 代码(比如 MKStoreKit),其中值得推荐一个就是 ASHTTPRequest 的替代者: MKNetworkKit。它支持 ARC 和块,易于使用且极为高效。
以下内容摘自 Mugunth自己的博客。原文位于:http://blog.mugunthkumar.com/products/ios-framework-introducing-mknetworkkit。
假设有一个网络框架,它能自动为你缓存 respones,能在你离线时自动记忆你的操作,你觉得怎样?
当你离线时,你可以收藏某个 tweet 页或者标记某个 feed 为已读,当你再次上线时,网络框架会自动执行你的这些操作,这一切都不需要你额外编写代码。请看我对于MKNetworkKit 框架的介绍。
什么是MKNetworkKit?
MKNetworkKit是一个 O-C 编写的网络框架,支持块,ARC 且用法简单。
MKNetworkKit 集 ASIHTTPRequest 和 AFNetworking 两个框架于一体。在集成二者的优秀特性之外,还增加了一堆新的功能。尤其是,相比起其它框架,它能让你更轻松地编写代码。它让你彻底远离那些恶心的网络代码。
超轻量级框架
整个框架只有 2 个类和一些类别方法。因此,它的使用极其简单。
在整个程序中只有一个全局队列
高度依赖互联网连接的 app 应该优先考虑网络线程的并发数。不幸的是,没有任何网络框架在这方面做得够好。因此,一旦你在程序中没有控制好网络线程的并发数,就极易导致出错。
假设你要上传一堆图片到服务器上。绝大多数移动网络(3G)不会允许你对同一个IP 地址的 HTTP 并发连接数超过 2 个。换句话说,在设备上,你不能从 3G 网络中获得 2 个以上的 HTTP 并发连接。对于 Edge 则更糟,大多数情况不能超过1 个。相比较家用宽带网络(Wifi),则这个限制要宽得多(6 个)。但是,你不可能总是使用 wifi,你必须也考虑到有限网络(窄带)的连通性。更多的时候,iDevice设备几乎都能连接到 3G 网络,因此,你同时只能上传 2 张图片。但是,真正的问题不是缓慢的上传速度,而是另一种情况。在你打开一个
view 试图加载缩略图(不同的view)时,上传线程被运行到后台。如果你没有控制好上传队列中的线程数,你的缩略图会加载超时。这是不正常的。正确的方式是优化缩略图加载线程,或者让线程等待直到上传完成再加载缩略图。这需要你在整个程序中只拥有一个queue 队列。
MKNetworkKit 在它的每个实例中使用单例来保证这一点。并不是说MKNetworkKit 是单例的,而是说它的共享队列是单例的。
正确显示网络状态指示
许多第 3 方框架都通过一个“网络连接数增加/减少”的方法回调来显示网络状态,MKNetworkKit则由于使用了单例的共享队列,能自动显示网络状态。在共享队列中有一个线程通过 KVO 方式会随时观察 operationCount 属性。因此对于开发者,一般情况下根本不需要操心网络状态的显示。
if (object == _sharedNetworkQueue && [keyPath isEqualToString:@"operationCount"]) {
[UIApplication sharedApplication].networkActivityIndicatorVisible =
([_sharedNetworkQueue.operations
count] & 0);
自动改变队列大小
如前所述,绝大部分移动网络不允许 2 个以上的并发连接,因此你的队列大小在3G 网络下应当设置为 2。 MKNetworkKit 会自动为你处理好这个。当网络出于3G/EDGE/GPRS 时,它会将并发数调整到 2。当网络处于 Wifi 网络时,则自动调整到 6。当你通过 3G 网络中从远程服务器加载缩略图时,这种调整能带来极大的好处。
MKNetworkKit 能自动缓存你所有的 GET 请求。当你再次发起同样的请求时,MKNetworkKit 随即就能调用 response缓存(如果可用的话)传递给 handler 进行处理。当然,它同时也向服务器发出请求。一旦获得服务器数据,handler 被再次要求处理新获取的数据。也就是说,你不用手动缓存。你只需要使用:
[[MKNetworkEngine sharedEngine] useCache];
当然,你可以覆盖这个方法(子类化),定制你的缓存路径和缓存占用的内存开销。
冻结网络操作
MKNetworkKit 能够“冻结”网络操作。在一个网络操作被“冻结”的情况下,一旦网络连断开,它们将自动序列化并在设备再次连线时自动被提交一次。类似 twitter 客户端的“drafts”。
当你提交一篇 tweet 时,如果网络被标记为“可冻结”,MKNetworkKit 会自动执行冻结并储存这些请求。因此会在将来推迟发送这篇 tweet。整个过程不需要你写一行代码。这个特性你可以用于其他操作,诸如收藏一篇 tweet 或者从 Goolge reader 客户端共享一个帖子,加一个链接到Instapaper 中,等等。
类似的请求只执行一个操作
当你加载缩略图(针对 twitter stream)时,你最终得为每个实际的图片创建一个新的请求。实际上你所进行的多个请求都是同一个URL。MKNetworkKit 对于队列中的每个 GET 请求都只会执行一次。它还不能到缓存 POST 请求。
MKNetworkKit 内置了缩略图缓存。只要覆盖几个方法,就可以设置内存中最大能缓存的图片数量,以及缓存要保存到目录。当然,你也可以不覆盖这些方法。
即速度。MKNetworkKit 缓存是内置的,就如 NSCache,当发现有内存警告,缓存到内存中的数据将被写入缓存目录。
完全支持 ARC
一般你只会在新项目中使用新的网络框架。MKNetworkKit并不意味着要放弃已有的框架(当然你也可以放弃,这会是个乏味的工作)。对于新的项目,你总是想使用 ARC。当你看到本文的时候,很可能 MKNetworkKit
会是仅有的完全支持 ARC 的网络框架。ARC 通常比非 ARC 代码更快。
Ok,我就不“自卖自夸”了。让我们立即了解如果使用这个框架。
添加MKNetworkKit
将 MKNetworkKit 目录拖到项目中添加下列框架: CFNetwork.Framework, SystemConfiguration.framework, Security.framework and ImageIO.Framework.将 MKNetworkKit.h 头文件包含到 PCH 文件中对于 iOS,删除 NSAlert+MKNetworkKitAdditions.h对于 Mac,删除 UIAlertView+MKNetworkKitAdditions.h
总共只需要 5 个核心文件,真是一个强大的网络开发包
MKNetworkKit 的类
MKNetworkOperationMKNetworkEngine一些工具类 (Apple 的 Reachability) 以及类别
我喜欢简单。苹果已经写了最基本最核心的网络代码。第 3 方框架需要的是提供一个优雅的网络队列最多再加上缓存。我认为第3 方框架不应该超过 10 个类(无论它是网络的还是 UIKit 还是别的什么)。超过这个数就太臃肿了。Three20 就是一个例子。现在 ShareKit 又是这样。尽管它们是优秀的,但仍然是庞大和臃肿的。ASIHttpRequest or AFNetworking 比 RESTKit 更轻,JSONKit比TouchJSON (或者任何 TouchCode 库)更轻。这只是我自己的看法,但当一个第三方库的代码超过程序源代码1/3,我就不会使用它。
框架臃肿带来的问题是很难理解它的内部工作机制,以及很难根据自己的需求定制它(当你需要时)。我曾经写过的一些框架(例如MKStoreKit ,用于应用程序内购的 )总是易于使用,我认为MKNetworkKit 也应该是这样。对于 MKNetworkKit ,你所需要了解的就是暴露在两个类MKNetworkOperation 和 MKNetworkEngine 中的方法。MKNetworkOperation 就好比ASIHttpRequest类。它是一个NSOperation 子类,封装了你的 request 和
response 类。对于每个网络操作,你需要创建一个MKNetworkOperation 。
MKNetworkEngine 是一个伪单例类,管理程序中的网络队列。它是伪单例的,也就是说,对于简单请求,你可以直接用MKNetworkEngine 中的方法。要进行深度的定制,你应该进行子类化。每个 MKNetworkEngine 子类有它自己的Reachability 对象,用于通知它来自服务器的reachability 通知。对于不同的 REST 服务器,你可以考虑创建单独的 MKNetworkEngine子类。
它是伪单例,它的子类的每个请求都共用唯一的一个队列。你可以在应用程序委托中retain 这个 MKNetworkEngine ,就像CoreData 的 managedObjectContext 类一样。在使用MKNetworkKit 时,创建一个 MKNetworkEngine 子类将你的网络请求进行逻辑上的分组。例如,将所有关于 Yahoo 的方法放在一个类,所有 Facebook 有关的方法放进另一个类。来看 3 个实际使用的例子。
“YahooEngine” 从 Yahoo 财经服务器抓取货币汇率。
步骤 1:创建YahooEngine 类继承于MKNetworkEngine。MKNetworkEngine 使用主机名和指定的头(如果有的话)进行初始化。头信息可以是nil。如果你是在自己的 REST 服务器上,你可以考虑加一个客户端 app 的版本或者其他信息(比如客户端的标识)。
NSMutableDictionary *headerFields = [NSMutableDictionary dictionary];
[headerFields
setValue:@"iOS"forKey:@"x-client-identifier"];
self.engine = [[YahooEngine alloc] initWithHostName:@"download.finance.yahoo.com"
customHeaderFields:headerFields];
注意,yahoo 并不识别你在头中发送x-client-identifier 给它,这个示例仅仅是演示这个特性而
由于使用了 ARC 代码,作为开发者你需要拥有(强引用)Engine对象。
一旦你创建了一个 MKNetworkEngine子类, Reachability 即自动实现。当你的服务器由于某些情况挂了,主机名不可访问,你的请求会自动被冻结。关于“冻结”,请参考后面的“冻结操作”小节。
步骤 2:设计Engine 类 (关注分离)
现在,开始编写 Yahoo Engine 中的方法,以抓取汇率。这些方法将在ViewController 中被调用。良好的设计体验是确保不要将 engine 类中的 URL/HTTPHeaders 暴露给调用者。你的视图不应该知道URL 或者相关的参数。也就是,只需要向 engine 方法传递货币种类和货币单位就可以了。方法的返回值可能是 double,即汇率,以及获取汇率的时间。由于是异步操作,你应当在块中返回这些值。例如:
-(MKNetworkOperation*) currencyRateFor:(NSString*) sourceCurrency
inCurrency:(NSString*) targetCurrency
onCompletion:(CurrencyResponseBlock) completion
onError:(ErrorBlock)
在父类 MKNetworkEngine 中,定义了3 个块类型:
typedef void (^ProgressBlock)(double progress);
typedef void (^ResponseBlock)(MKNetworkOperation* operation);
typedef void (^ErrorBlock)(NSError* error);
在 YahooEngine中,我们使用了一个新的块类型:CurrencyResponseBlock,用以返回汇率。其定义如下:
typedef void (^CurrencyResponseBlock)(double rate);
在其他正式的 app 中,你应该定义自己的块类似于CurrencyResponseBlock ,用以向 ViewController 返回数据。
步骤 3:处理数据
处理数据,包括将从服务器抓来的数据(例如 JSON/XML/plists)进行数据类型转换。这应当在 Engine 中完成。注意,不要在控制器中完成。你的 Engine 应当将数据以适当的模型对象或模型对象的数组返回。在engine 中转换 JSON/XML 为模型——注意,适当保持关注分离,view controller 不应当知道任何用于访问 JSON 节点的 key。这种思想主导了engine 的设计。许多网络框架并不强制要求你服从关注分离,我们这样做,是因为我们为你考虑到了。
步骤 4:实现方法
现在,我们来讨论方法实现细节。要从 Yahoo 获得汇率信息,最简单的是发起一个 GET 请求。下列宏用一对指定的货币格式化 URL 字串:
We will now discuss the implementationdetails of the method that calculates your currency exchange.
Getting currency information from Yahoo,is as simple as making a GET request.
I wrote a macro to format this URL for a given currency pair.
#define YAHOO_URL(__C1__, __C2__) [NSString stringWithFormat:@"d/quotes.csv?e=.csv&f=sl1d1t1&s=%@%@=X", __C1__, __C2__]
按如下顺序编写 engine类方法:
根据参数准备 URL创建一个 MKNetworkOperation 对象设置方法参数设置 operation 的 completion 块和 error 块(在 completation 块中处理 response 并转换为模型)可选地,添加一个 progress 块(或者在 view controller 中做这个)如果 operation 是下载,设置下载流(通常是文件)。这步也是可选的当 operation 完成,处理结果并调用方法块,并将数据返回给调用者。
示例代码如下:
MKNetworkOperation *op = [selfoperationWithPath:YAHOO_URL(sourceCurrency, targetCurrency)
params:nil
httpMethod:@"GET"];
[op onCompletion:^(MKNetworkOperation*completedOperation)
DLog(@"%@", [completedOperation responseString]);
//do your processing here
completionBlock(5.0f);
}onError:^(NSError* error) {
errorBlock(error);
[self enqueueOperation:op];
上述代码格式化 URL 并创建了 MKNetworkOperation。设置完 completion 和 error 块之后,将 operation 加入到队列(通过父类的 enqueueOperation 方法),然后返回一个 operation 的引用。因此,如果你在 viewDidAppear 中调用这个方法,则在 viewWillDisappear 方法中取消operation。取消 operation 将释放 operation 以便执行 queue 中用于其他view 的 operation(牢记,在移动网络中只有2
个 operation 能被同时进行,当 operation 不再需要时取消它们能提升 app 的性能和速度)。
在 viewcontroller 中也可以添加一个 progress 块用以刷新UI。例如:
[self.uploadOperation onUploadProgressChanged:^(double progress) {
DLog(@"%.2f", progress*100.0);
self.uploadProgessBar.progress =
MKNetworkEngine 也有一个只用 URL 创建 operation 的有用方法。因此第1行代码也可以写成:
MKNetworkOperation *op = [self operationWithPath:YAHOO_URL(sourceCurrency,
targetCurrency)];
注意,请求的 URL将自动添加上主机名(在 engine 实例化时指定的)。
像这样的实用方法 MKNetworkEngine还有许多,你可以查看头文件。
上传图片到服务器 (例如 TwitPic)。
现在让我们看一个上传图片到服务器的例子。要上传图片,显然要 operation 能编码 multi-part 表单数据。 MKNetworkKit 使用类似 ASIHttpRequest 的方式。
你可以非常简单地通过MKNetworkOperation 的 addFile:forKey:方法将一个文件作为请求中的 multi-part 表单数据提交。
MKNetworkOperation 也有一个方法,可以将图片以 NSData 的方式提交。即 addData:forKey: 方法,它可以将图片以NSData 的方法上传到服务器。 (例如直接从相机中捕获的图片).
下载文件到本地目录 (缓存)
使用MKNetworkKit 从服务器下载文件并保存到 iPhone 的本地目录非常简单。
只需要设置 MKNetworkOperation的 outputStream 。
[operation setDownloadStream:[NSOutputStream
outputStreamToFileAtPath:@"/Users/mugunth/Desktop/DownloadedFile.pdf"
append:YES]];
你可以设置多个 outputStream 到一个 operation,将同一文件保存到几个地方(例如其中一个是你的缓存目录,另一个用做你的工作目录)。
缓存图片的缩略图
对于下载图片,你可能需要提供一个绝对 URL 地址而不是一个路径。
MKNetworkEngine 的operationWithURLString:params:httpMethod: 方法根据绝对 URL地址来创建网络线程。
MKNetworkEngine 相当聪明。它会将同一个 URL 的多次 GET 请求合并成一个,当 operation 完成时它会通知所有的块。这显著提升了抓取图片 URL 以渲染缩略图的速度.
子类化 MKNetworkEngine然后覆盖图片的缓存目录及缓存的大小。如果你不想定制这二者,你可以直接调用 MKNetworkEngine中的方法来下载图片。这是我极力推荐的。
缓存operation
MKNetworkKit 默认会缓存所有请求。你所需要的仅仅是在你自己的 engine 中打开它。当执行一个 GET 请求时,如果上次的 response 已缓存,相应的 completion 块将用缓存的response 进行调用(瞬间)。要想知道 response 是否缓存,可以调用 isCachedResponse 方法,如下所示:
[op onCompletion:^(MKNetworkOperation *completedOperation) {
if([completedOperation isCachedResponse]) {
DLog(@"Data from cache");
DLog(@"Data from server");
DLog(@"%@", [completedOperation responseString]);
onError:^(NSError* error) {
errorBlock(error);
冻结operation
MKNetworkKit 的一个最有趣的特性是它内置的冻结 operation 特性。你只需要设置 operation 的 freeesable 属性就可以。几乎什么也不用做!
[op setFreezable:YES];
冻结是指 operation 在网络被断开时自动序列化并在网络恢复后自动执行。例如当你离线时也能够进行收藏tweet 的操作,然后在你再次上线时 operation 自动恢复执行。
在应用程序进入后台时,冻结的 operation 也会被持久化到磁盘。然后在应用程序回到前台后自动恢复执行。
MKNetworkOperation 中的有用方法
如下所示,MKNetworkOperation 公开了一些有用的方法,你可从中获取各种格式的 response 数据:
responseDataresponseStringresponseJSON (Only on iOS 5)responseImageresponseXMLerror
当 operation 执行完时,这些方法被用于获取响应数据。如果格式不正确,方法会返回nil。例如,响应的数据明明是一个 HTML 格式,你用 responseImage 方法只会得到 nil。只有 responseData 能保证无论什么格式都返回正确,而其他方法你必须确保和相应的repsone 类型匹配。
DLog 和 ALog 宏被无耻地从 Stackoverflow 剽窃来了,我找不到源作者。如果是你写的,请告诉我。
关于GCD 的一点说明
因为网络线程有可能会能被停止或优先处理,我果断放弃了 GCD——GCD 的效率是比NSOperation 高,但它做不到这一点。我建议在你的网络线程中也不要使用基于 GCD 的队列。
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
在本周二凌晨召开的苹果年度开发者大会WWDC上,苹果公司推出了全新的编程语言Swift。Swift 基于C和Objective-C,是供iOS和OS X应用编程的全新语言,更加高效、现代、安全,可以提升应用性能,同时降低开发难度。
据称,Swift仍然处于beta测试的阶段,会在iOS 8发布的时一同推出市场,用来取代现有的Objective-C语言。Swift推出之后,苹果公司也不会停止对Objective-C的支持,开发工具会同时支持两种语言。
WWDC刚刚结束,在不到24小时的时间内,已经有开发者使用 Switf 编程语言完成了一款克隆 Flappy Bird 的小游戏,并。刚刚,2048游戏也来了:。
目前,这个项目已经收获了3000多个称赞和800多个派生。
继Flappy Bird之后,越来越多的开源应用迅速的完成了向Swift语言的转换, 是Github上使用Swift语言的开源项目列表,目测已经有200多个了!
下面是我们收集的一些Swift编程语言的相关资料:
苹果官方Swift文档《》网友整理的Swift中文文档《 》 极客学院《》课程国内第一个Swift社区:
相关文章:
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
http://repo1.maven.org/maven2/com/google/zxing/android-integration/2.2/
https://code.google.com/p/zxing/downloads/list
  因为手机端的输入不是很方便,所以条形码/二维码的扫描是一种很有效的解决手段。
  比较流行的手机应用中,常用的二维码扫描实现,是使用了开源库——ZXing:
  据项目主页的介绍,这个开源项目是用Java实现的,用手机自带的摄像头,扫描条形码/二维码,不需要和服务器通信(当然这个项目也可以用于PC和服务器),就可以做一维或二维码的识别。
  支持的各种码格式很多:
  下面就介绍一下如何编译运行这个项目。
  去下载最新的项目:命名是ZXing-x.y.zip
  目前()最新的版本号是2.2:
ZXing-2.2.zip:=
  下载后,解压得到的目录如下:
  我们其实只用到了android和core两个目录。
直接使用编译好的Jar包
  上说的,如果你想直接使用编译好的jar包,那么直接去: 下载jar包即可。
  我们这里需要的是core下的jar包。下载core-2.2.jar即可。
  然后,在Eclipse中,导入zxing-2.2\android下的项目,导入后会有一些错误。
  新建libs目录,将刚才下载的core-2.2.jar拷贝进去,错误就都消失了。
  然后就直接右键项目Run即可。
  如果想自己编译,也算是很方便。下面讲讲。
编译工具准备
  首先得准备两个工具:
  Maven:
  下载链接:
  Maven:
  下载后放在合适的路径下解压缩,然后将bin目录添加到环境变量Path中即可。两个都是。
  可以在cmd中查看版本号来验证配置是否成功:
手动编译的配置
  首先,从命令行进入到zxing-2.2目录下:
  输入如下命令:
  android update project --path android
  成功后就可以看到zxing-2.2\android目录下新增了一个名为local.properties的文件,里面内容是:sdk.dir=你的Android SDK目录。
  首先编译core,方法如下:
  命令行进入到zxing-2.2\core路径下,输入命令:
  mvn -DskipTests -Dgpg.skip=true install
  运行完之后core目录下会生成一个target目录。
  里面就有编译好的各种东西,包括文档、需要的Jar包等(比如我的这个叫core-2.3-SNAPSHOT.jar)。
编译Android项目
  首先,把刚才生成的jar包(core-2.3-SNAPSHOT.jar)拷贝进zxing-2.2\android目录下的libs目录。
  然后,命令行进入到zxing-2.2\android路径下,输入命令:
  ant debug
  生成的bin目录下就包含了.apk。
  (如果关联了360手机助手或者豌豆荚,USB连接手机后,双击这个.apk即可安装应用,因为是debug版的,所以会提示是山寨应用。)
  当然,也可以将Android程序导入Eclipse,libs中添加Jar包之后右键项目来编译和运行,如前所述。
  ZXing项目地址:
  GettingStarted:
  博客:
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
                  Android平台下利用zxing实现二维码开发
  现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平台上应用比较成熟,而在Android平台上主流还是用zxing库,因此这里主要讲述如何利用zxing进行二维码开发。
1.如何将zxing的Android源码导入工程。
  在导入zxing的android源码之前,先去官方下载zxing的源码:。
  我这里下载的是1.6版本的,我试验了几个版本,发现2.0以后的版本实际用起来没有1.6和1.7版本的好用,最后选择了1.6版本。
  zxing 1.6源码结构如下:
  其中android文件夹就是android平台下的官方例子。
  在导入之前先要对core文件下的源码进行编译,得到核心包core.jar。
  编译方法请参照:
  然后就可以导入android平台下的例子了,导入方法如下:
  1)打开Eclipse,新建android项目:(注意不要直接把android文件夹拷到workspace下导入,那样会无法导入)
  2)导入核心包core.jar。
  3)修改strings.xml文件。在导入core.jar之后工程还是会有错误:
  出现这种错误可能是由于字符错误导致的,只需要把所有的%s 和%f改成 %1s和f
  修改完之后重新清理项目,此时已经没有错误了:
  4)运行效果:
2.代码简化
  上面代码中,很多功能我们在自己的项目中都用不到,因此需要对其进行简化,至于如何简化这里就不赘述了,网上有很多教程。下面只给出简化后的结果:
  如果只进行二维码识别和二维码生成的话,只需要上面包中的文件。其中CaptureActivity.java是拍照取景的类,camera包下面的类主要与照相机相关的类,decoding和encoding是解码和编码相关的类,view是取景框相关的类。
3.将简化的zxing代码嵌入自己的工程。
  在自己的工程中嵌入简化的zxing代码即可实现二维码生成和识别功能。
  嵌入方法:
  1)将上述简化的代码拖到自己工程目录下;
  2)将values文件夹和raw文件夹复制自己工程目录下;
  3)建立CaptureActivity.java的布局文件capture.xml。
&?xml version="1.0" encoding="utf-8"?&
&FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" &
&SurfaceView
android:id="@+id/preview_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" /&
&com.zxing.view.ViewfinderView
android:id="@+id/viewfinder_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" /&
&RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:orientation="vertical" &
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:gravity="center"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="Scan Barcode"
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold" /&
android:id="@+id/btn_cancel_scan"
android:layout_width="230dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"
android:layout_marginBottom="75dp"
android:text="Cancel"
android:textSize="15sp"
android:textStyle="bold" /&
&/RelativeLayout&
&/FrameLayout&
  3)导入core.jar包
  4)修改AndrodMainfest.xml
&?xml version="1.0" encoding="utf-8"?&
&manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qrcode"
android:versionCode="1"
android:versionName="1.0"&
&uses-sdk android:minSdkVersion="7" /&
&uses-permission android:name="android.permission.VIBRATE" /&
&!-- 震动权限 --&
&uses-permission android:name="android.permission.CAMERA" /&
&uses-feature android:name="android.hardware.camera" /&
&!-- 使用照相机权限 --&
&uses-feature android:name="android.hardware.camera.autofocus" /&
&!-- 自动聚焦权限 --&
&application android:icon="@drawable/icon" android:label="@string/app_name"&
&activity android:name=".MainActivity"
android:label="@string/app_name"&
&intent-filter&
&action android:name="android.intent.action.MAIN" /&
&category android:name="android.intent.category.LAUNCHER" /&
&/intent-filter&
&/activity&
&!-- 隐藏键盘 --&&!-- 全屏 --&
android:configChanges="orientation|keyboardHidden"
android:name="com.zxing.activity.CaptureActivity"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:windowSoftInputMode="stateAlwaysHidden" &
&/activity&
&/application&
&/manifest&
  5)运行效果:
  在这里识别二维码之后是把结果返回到上一个activity显示,所以在MainActivity中调用方法是:
Intent openCameraIntent = new Intent(MainActivity.this,CaptureActivity.class);
startActivityForResult(openCameraIntent, 0);
  然后获取结果在onActivityResult中进行:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
String scanResult = bundle.getString("result");
resultTextView.setText(scanResult);
  如果要直接在CaptureActivity中弹出识别结果,可以更改CaptureActivity.java的handleDecode方法:
public void handleDecode(Result result, Bitmap barcode) {
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
String resultString = result.getText();
if (resultString.equals("")) {
Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
System.out.println("Result:"+resultString);
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("result", resultString);
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);
CaptureActivity.this.finish();
  上述代码是得到结果之后传给之前调用CaptureActivity的activity,所以如果要改的话只需要把else语句块下面的语句和最后一句finish注释掉,补上自己的代码就可以了。比如可以改成:
public void handleDecode(Result result, Bitmap barcode) {
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
final String resultString = result.getText();
if (resultString.equals("")) {
Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
System.out.println("Result:"+resultString);
/*Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("result", resultString);
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);*/
AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create();
resutlDialog.setTitle("扫描结果");
resutlDialog.setMessage(resultString);
resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
dialog.dismiss();
resutlDialog.show();
//CaptureActivity.this.finish();
  我的工程源码下载:
3.关于zxing源码的若干问题。
  在实际使用过程中发现zxing源码存在一些问题,下面逐一来说一下:
  1)竖屏问题。
  zxing给出的官方例子是横屏的,但是对于手机实际上竖屏操作更加方便一点,如果要改成竖屏,需要修改以下几个文件:
  首先将AndrodMainfest.xml下CaptureActivity的配置改为竖屏:
android:screenOrientation="portrait"
  然后需要修改CameraConfigurationManager.java下的setDesiredCameraParameters方法,该方法直接使用
camera.setDisplayOrientation(90);
  来让屏幕旋转90度,这个办法在android 2.2版本以后是可行的,但是2.2之前的版本并没有提供这个接口,因此需要对sdk版本进行判断:
if (Integer.parseInt(Build.VERSION.SDK) &=8)
setDisplayOrientation(camera, 90);
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
parameters.set("orientation", "portrait");
parameters.set("rotation", 90);
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
parameters.set("orientation", "landscape");
parameters.set("rotation", 90);
  在修改为竖屏之后发现取景框会发生拉伸,需要进行以下几点修改:
  在CameraManager.java中将:
rect.left = rect.left * cameraResolution.x / screenResolution.x;
rect.right = rect.right * cameraResolution.x / screenResolution.x;
rect.top = rect.top * cameraResolution.y / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
  修改为:
rect.left = rect.left * cameraResolution.y / screenResolution.x;
rect.right = rect.right * cameraResolution.y / screenResolution.x;
rect.top = rect.top * cameraResolution.x / screenResolution.y;
rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
  然后将DecodeHandler类中的方法decode改为:
private void decode(byte[] data, int width, int height) {
long start = System.currentTimeMillis();
Result rawResult = null;
//modify here
byte[] rotatedData = new byte[data.length];
for (int y = 0; y & y++) {
for (int x = 0; x & x++)
rotatedData[x * height + height - y - 1] = data[x + y * width];
int tmp = // Here we are swapping, that's the difference to #11
PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
rawResult = multiFormatReader.decodeWithState(bitmap);
} catch (ReaderException re) {
// continue
} finally {
multiFormatReader.reset();
if (rawResult != null) {
long end = System.currentTimeMillis();
Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString());
Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, rawResult);
Bundle bundle = new Bundle();
bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap());
message.setData(bundle);
//Log.d(TAG, "Sending decode succeeded message...");
message.sendToTarget();
Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
message.sendToTarget();
  再将CameraConfigurationManager类中的方法initFromCameraParameters修改为:
void initFromCameraParameters(Camera camera) {
Camera.Parameters parameters = camera.getParameters();
previewFormat = parameters.getPreviewFormat();
previewFormatString = parameters.get("preview-format");
Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
screenResolution = new Point(display.getWidth(), display.getHeight());
Log.d(TAG, "Screen resolution: " + screenResolution);
Point screenResolutionForCamera = new Point();
screenResolutionForCamera.x = screenResolution.x;
screenResolutionForCamera.y = screenResolution.y;
// preview size is always something like 480*320, other 320*480
if (screenResolution.x & screenResolution.y) {
screenResolutionForCamera.x = screenResolution.y;
screenResolutionForCamera.y = screenResolution.x;
cameraResolution = getCameraResolution(parameters, screenResolutionForCamera);
//cameraResolution = getCameraResolution(parameters, screenResolution);
Log.d(TAG, "Camera resolution: " + screenResolution);
  最后将PlanarYUVLuminanceSource.java中的renderCroppedGreyscaleBitmap方法改为:
public Bitmap renderCroppedGreyscaleBitmap() {
int width = getWidth();
int height = getHeight();
int[] pixels = new int[width * height];
byte[] yuv = yuvD
int inputOffset = top * dataHeight +
for (int y = 0; y & y++) {
int outputOffset = y *
for (int x = 0; x & x++) {
int grey = yuv[inputOffset + x] & 0xff;
pixels[outputOffset + x] = 0xFF000000 | (grey * 0x);
inputOffset += dataH
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
  3)取景框的绘制
  取景框的绘制可以参照这篇博文:
  4)闪光灯的开启和关闭
  如果需要开启和关闭闪光灯,在CameraManager.java中添加2个方法:
  public void openLight()
//打开闪光灯
if(camera!=null)
Parameters parameter=camera.getParameters();
parameter.setFlashMode(Parameters.FLASH_MODE_TORCH);
camera.setParameters(parameter);
public void closeLight()
//关闭闪光灯
if(camera!=null)
Parameters parameter=camera.getParameters();
parameter.setFlashMode(Parameters.FLASH_MODE_OFF);
camera.setParameters(parameter);
  然后在CaptureActivity中的initCamera方法中打开闪光灯:
CameraManager.get().openDriver(surfaceHolder);
CameraManager.get().openLight();
//开闪光灯
  在需要的地方关闭闪光灯即可。
  5)连续扫描问题
  如果在识别二维码成功之后,需要连续多次扫描二维码,只需在扫描完成之后添加代码(在CaptureActivity的dandleDecode方法中添加):
if(handler!=null)
//实现连续扫描
handler.restartPreviewAndDecode();
  比如:
public void handleDecode(Result result, Bitmap barcode) {
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
final String resultString = result.getText();
if (resultString.equals("")) {
Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
System.out.println("Result:"+resultString);
/*Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("result", resultString);
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);*/
AlertDialog resutlDialog = new AlertDialog.Builder(CaptureActivity.this).create();
resutlDialog.setTitle("扫描结果");
resutlDialog.setMessage(resultString);
resutlDialog.setButton(AlertDialog.BUTTON_POSITIVE, "打开链接", new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
dialog.dismiss();
if(!isLegalUrl(resultString))
//如果url不合法
Toast.makeText(getApplicationContext(), "该链接不是合法的URL", Toast.LENGTH_SHORT).show();
if(handler!=null)
//实现连续扫描
handler.restartPreviewAndDecode();
Intent intent = new Intent();
//打开链接
intent.setAction("android.intent.action.VIEW");
Uri content_url = Uri.parse(resultString);
intent.setData(content_url);
startActivity(intent);
resutlDialog.setButton(AlertDialog.BUTTON_NEGATIVE, "取消", new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which)
dialog.dismiss();
if(handler!=null)
//实现连续扫描
handler.restartPreviewAndDecode();
resutlDialog.show();
//CaptureActivity.this.finish();
  6)识别完成之后的震动。
  如果需要取消二维码识别之后的震动,只需要在CaptureActivity类的onResume方法中将vibrate 设置为false即可。
  7)修改取景框距屏幕顶部位置
  如果需要修改取景框距屏幕顶部位置,只需要修改CameraManager.java的getFramingRect方法,在getFramingRect方法中,
int topOffset = (screenResolution.y - height) / 2
  这句是控制取景框到屏幕顶部的距离,若需要减小距屏幕顶部的距离,只需要将分母变大即可。
  8)取景框下方提示文字的绘制。
  在ViewfinderView.java的 Collection&ResultPoint& currentPossible = possibleResultPoints前面加入以下代码:
TextPaint textPaint = new TextPaint();
textPaint.setARGB(0xFF, 0xFF, 0xFF,0xFF);
//字体颜色
textPaint.setTextSize(TEXT_SIZE * density);
textPaint.setAntiAlias(true);
//设置抗锯齿,否则字迹会很模糊StaticLayout layout = new StaticLayout(getResources().getString(R.string.scan_text),textPaint,
frame.right-frame.left,Alignment.ALIGN_NORMAL,1.0F,0.0F,true);
canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density)); //绘制起始位置
layout.draw(canvas);
  这里解释一下,textPaint.setAntiAlias(true); 是设置为抗锯齿,否则字体会很模糊。StaticLayout的第一个参数就是要绘制的字符串,第二个是画笔,第三个参数是设置每一行的宽度,即超过该宽度就换行,第四个是对齐方式。
  canvas.translate(frame.left, (float) (frame.bottom + (float)TEXT_PADDING_TOP *density))的参数是绘制字符串的起始位置。
  9)乱码问题
  在扫描一部分二维码时会出现乱码的情况,是由于编码的问题,可以参照这篇文章:
  如果不想编译core包,我的工程源码中的core包是已经改好了的,可以识别GBK二维码,需要的朋友可以直接拿去用。
  关于zxing 二维码识别的问题暂时就讨论这么多,有兴趣的朋友可以读源码深入研究一下。
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
a. AVFoundation
b. AudioToolbox
c. CoreVideo
d. CoreMedia
e. libiconv
f. AddressBook
g. AddressBookUI
二维码识别
使用方法一
#import &ZXingWidgetController.h&
#import &QRCodeReader.h&
- (IBAction)scanPressed:(id)sender {
ZXingWidgetController *widController = [[ZXingWidgetController alloc] initWithDelegate:self showCancel:YES OneDMode:NO];
NSMutableSet *readers = [[NSMutableSet alloc ] init];
QRCodeReader* qrcodeReader = [[QRCodeReader alloc] init];
[readers addObject:qrcodeReader];
[qrcodeReader release];
widController.readers = readers;
[readers release];
[self presentModalViewController:widController animated:YES];
[widController release];
@protocol ZXingDelegate
- (void)zxingController:(ZXingWidgetController*)controller didScanResult:(NSString *)result;
- (void)zxingControllerDidCancel:(ZXingWidgetController*)controller;
使用方法二
#import "Decoder.h"
#import "TwoDDecoderResult.h"
#import "QRCodeReader.h"
- (void)viewDidLoad {
// setup QR reader
self.qrReader = [[NSMutableSet alloc ] init];
QRCodeReader* qrcodeReader = [[QRCodeReader alloc] init];
[self.qrReader addObject:qrcodeReader];
self.scanningQR = NO;
self.step = STEP_QR;
// AVFoundation的回调函数
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
// 第一步,将sampleBuffer转成UIImage
UIImage *image= [self getCaptureImage:sampleBuffer];
// 第二步,用Decoder识别图象
Decoder *d = [[Decoder alloc] init];
d.readers = self.qrReader;
d.delegate = self;
self.scanningQR = [d decodeImage:image] == YES ? NO : YES;
@protocol DecoderDelegate&NSObject&
- (void)decoder:(Decoder *)decoder willDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset;
- (void)decoder:(Decoder *)decoder didDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset withResult:(TwoDDecoderResult *)result {
NSLog(@"result = %@", [result text]);
- (void)decoder:(Decoder *)decoder failedToDecodeImage:(UIImage *)image usingSubset:(UIImage *)subset reason:(NSString *)reason;
- (void)decoder:(Decoder *)decoder foundPossibleResultPoint:(CGPoint)point;
Trouble Shoot & Tips
ZXing和OpenCV的兼容问题
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
阅读:1053
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
1、当屏幕旋转的时候Activity执行的方法为onpause()onSaveInstanceState()onStop()onDestory()oncreate()onStart()onRestoreInstanceState()onResume()2、如果想在屏幕改变的时候不执行这些通过查阅Android API可以得知android:onConfigurationChanged实际对应的是Activity里的onConfigurationChanged()方法。在AndroidManifest.xml中添加上诉代码的含义是表示在改变屏幕方向、弹出软件盘和隐藏软键盘时,不再去执行onCreate()方法,而是直接执行onConfigurationChanged()。如果不申明此段代码,按照Activity的生命周期,都会去执行一次onCreate()方法,而onCreate()方法通常会在显示之前做一些初始化工作。所以如果改变屏幕方向这样的操作都去执行onCreate()方法,就有可能造成重复的初始化,降低程序效率是必然的了,而且更有可能因为重复的初始化而导致数据的丢失。这是需要千万避免的。在AndroidManifest.xml中需要添加的代码为android:configChanges="orientation|keyboard|keyboardHidden|navigation|screenSize"3、个人认为我们如果要支持切换屏幕的方向那么最好监听到屏幕方向改变的代码放到onConfigurationChanged()方法中、其他功能的代码还是放到相应的回调方法中
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
阅读:1528
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
当我们在处理一系列线程的时候,当数量达到一定量,在以前我们可能会选择使用NSOperationQueue来处理并发控制,但如何在GCD中快速的控制并发呢?答案就是,对经常做unix开发的人来讲,我所介绍的内容可能就显得非常入门级了,信号量在他们的多线程开发中再平常不过了。  信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。  在GCD中有三个函数是semaphore的操作,分别是:  dispatch_semaphore_create   创建一个semaphore  dispatch_semaphore_signal   发送一个信号  dispatch_semaphore_wait    等待信号  简单的介绍一下这三个函数,第一个函数有一个整形的参数,我们可以理解为信号的总量,dispatch_semaphore_signal是发送一个信号,自然会让信号总量加1,dispatch_semaphore_wait等待信号,当信号总量少于0的时候就会一直等待,否则就可以正常的执行,并让信号总量-1,根据这样的原理,我们便可以快速的创建一个并发控制来同步任务和有限资源访问控制。
  简单的介绍一下这一段代码,创建了一个初使值为10的semaphore,每一次for循环都会创建一个新的线程,线程结束的时候会发送一个信号,线程创建之前会信号等待,所以当同时创建了10个线程之后,for循环就会阻塞,等待有线程结束之后会增加一个信号才继续执行,如此就形成了对并发的控制,如上就是一个并发数为10的一个线程队列。
作者:fhbystudy 发表于
https://blog.csdn.net/fhbystudy/article/details/
阅读:34203
https://blog.csdn.net/fhbystudy/article/details/
https://blog.csdn.net/fhbystudy/article/details/
在手机应用程序开发中,为了减少与服务端的交互次数,加快用户的响应速度,一般都会在iOS设备中加一个缓存的机制,前面一篇文章介绍了iOS设备的内存缓存,这篇文

我要回帖

更多关于 crashlytics ios 的文章

 

随机推荐