对于一个iOS新入门的菜鸟来说,如何快速入门?我以为直接调试和阅读开源代码是最有效的方式,
本羽就是按照这种方式进行的,因为是菜鸟,所以难免有些地方有错误,请指出,谢谢。内容的浅显是一个菜鸟的通病,我相信过一段时间的磨练,内容的深度会有所体现的。
这个开源项目主要功能是 将汉字转换成拼音的形式输出,其详细过程如下:以“经”为例,其解析过程如下:输入汉字“经”,转换为十进制32463,
然后以二进制整数形式0x7ECF存放(经过大写转换),按照k-v字典查询可得到其拼音jing1;缺点是无法正确处理多音字的问题;
感谢本开源项目作者,给予我一个学习的机会,其github为 https://github.com/kimziv/PinYin4Objc
涉及知识点
1. assign,copy,retain
@property (nonatomic, assign) NSString *title;
什么是assign,copy,retain之间的区别?
assign: 简单赋值,不更改索引计数(Reference Counting)。
copy: 建立一个索引计数为1的对象,然后释放旧对象
retain:释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
使用规则如下:
使用assign: 对基础数据类型 (NSInteger,CGFloat)和C数据类型(int, float, double, char, 等等)
使用copy: 对NSString
使用retain: 对其他NSObject和其子类
nonatomic关键字:
atomic是Objc使用的一种线程保护技术,在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。
2. 在OC中使用默认值的方法
可以用HanyuPinyinOutputFormat看出,可以利用set方法来设置变量的默认值,若不重写set方法,就使用默认值,若重写了set方法,就使用新值;
3. 其他一些细碎的小点
1> NSTimeInterval startTime=[[NSDate date] timeIntervalSince1970] 用该语句可以求出从UTC+0开始到现在所经过的秒数;这个数据有很大的作用:
若要求出某个函数的所花时间,可以在调用该函数前后,各调用一次该语句,然后求差得到结果;以前,为了测试lua程序的效率,就是使用该方式,效果很好;
4. 在应用中创建单例
什么是单例呢?
Wikipedia是如此定义的:
在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式。
当Apple引入了Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0),他们也引入了一个很适合用于实现单例模式的函数。
该函数就是dispatch_once:
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
该函数接收一个dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块。
dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题。
Apple的GCD Documentation证实了这一点:
如果被多个线程调用,该函数会同步等等直至代码块完成。
实际要如何使用这些呢?
好吧,假设有一个AccountManager类,你想在整个应用中访问该类的共享实例。你可以按如下代码简单实现一个类方法:
{
+ (ChineseToPinyinResource *)getInstance
static ChineseToPinyinResource *sharedInstance=nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance=[[self alloc] init];
});
return sharedInstance;
}
这就意味着你任何时候访问共享实例,需要做的仅是:
ChineseToPinyinResource *accountManager = [ChineseToPinyinResource sharedManager];
就这些,你现在在应用中就有一个共享的实例,该实例只会被创建一次。
该方法有很多优势:
1 线程安全
2 很好满足静态分析器要求
3 和自动引用计数(ARC)兼容
4 仅需要少量代码
该方法的劣势就是它仍然运行创建一个非共享的实例:
ChineseToPinyinResource *accountManager = [[ChineseToPinyinResource alloc] init];
有些时候你希望有这种行为,但如果正在想要的是仅一个实例被实例化就需要注意这点。
5. Unicode汉字编码表
1 unicode编码表
UNICODE只有一个字符集,中、日、韩的三种文字占用了Unicode中0x3000到0x9FFF的部分
Unicode目前普遍采用的是UCS-2,它用两个字节来编码一个字符,
比如汉字"经"的编码是0x7ECF,注意字符编码一般用十六进制来
表示,0x7ECF转换成十进制就是32463,UCS-2用两个字节来编码字符,两个字节就是16位二进制,
2的16次方等于65536,所以UCS-2最多能编码65536个字符。
编码从0到127的字符与ASCII编码的字符一样,比如字母"a"的Unicode
编码是0x0061,十进制是97,而"a"的ASCII编码是0x61,十进制也是97,
看一下Unicode对汉字的编码:
2 汉字编码表
U+ 0 1 2 3 4 5 6 7 8 9 A B C D E F
-----------------------------------------------------
4e00 一 丁 丂 七 丄 丅 丆 万 丈 三 上 下 丌 不 与 丏
4e10 丐 丑 丒 专 且 丕 世 丗 丘 丙 业 丛 东 丝 丞 丟
……
3 PinYin4Objc的解析过程
以“经”为例,其解析过程如下:输入汉字“经”,转换为十进制32463,然后以二进制整数形式0x7ECF存放(经过大写转换),按照k-v字典查询可得到其拼音jing1;
6. NSSearchPathForDirectoriesInDomains用法
IOS中的沙盒机制(SandBox)是一种安全体系,它规定了应用程序只能在为该应用创建的文件夹(iPhone为每一个应用程序生成的一个私有目录)内读取文件,不可以访问其他地方的内容。
所有的非代码文件都保存在这个地方,比如图片、声音、属性列表和文本文件等。
1.每个应用程序都在自己的沙盒内
2.不能随意跨越自己的沙盒去访问别的应用程序沙盒的内容
3.应用程序向外请求或接收数据都需要经过权限认证
所以通常使用Documents目录进行数据持久化的保存,而这个Documents目录可以通过:
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserdomainMask,YES) 得到。然后,判断该目录是否存在,若不存在,则创建;
7. 关于Block
Block变量,被__block修饰的变量称作Block变量。 基本类型的Block变量等效于全局变量、或静态变量。
Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事。需要注意的是由于Objective-C在iOS中不支持GC机制,使用Block必须自己管理内存,而内存管理正是使用Block坑最多的地方,错误的内存管理 要么导致return cycle内存泄漏要么内存被提前释放导致crash。
Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外、词法作用域以内的外部变量的值;Block与函数另一个不同的是,Block类似ObjC的对象,可以使用自动释放池管理内存(但Block并不完全等同于ObjC对象)。
换句话说,Block不仅 实现函数的功能,还能携带函数的执行环境。
NSArray *lines = [dictionaryText componentsSeparatedByString:@"\r\n"];
__block NSMutableDictionary *tempMap=[[NSMutableDictionary alloc] init];
@autoreleasepool {
[lines enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSArray *lineComponents=[obj componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[tempMap setObject:lineComponents[1] forKey:lineComponents[0]];
}];
}
分析:
其中,数组lines的数据格式如下:3007 (ling2), 4E00 (yi1), 4E01 (ding1,zheng1), 4E02 (kao3), ...
enumerateObjectsUsingBlock: 有很多新特性:
通常enumerateObjectsUsingBlock: 和 (for(... in ...)在效率上基本一致,有时会快些。主要是因为它们都是基于 NSFastEnumeration 实现的. 快速迭代在处理的过程中需要多一次转换,当然也会消耗掉一些时间. 基于Block的迭代可以达到本机存储一样快的遍历集合. 对于字典同样适用,而数组的迭代却不行。
注意"enumerateObjectsUsingBlock" 修改局部变量时, 你需要声明局部变量为 __block 类型.
8. Grand Central Dispatch(GCD)
1.下面来看下如何使用gcd编程的异步
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 处理耗时操作的代码块...
//通知主线程刷新
dispatch_async(dispatch_get_main_queue(), ^{
//回调或者说是通知主线程刷新,
});
});
dispatch_async开启一个异步操作,第一个参数是指定一个gcd队列,第二个参数是分配一个处理事物的程序块到该队列。
dispatch_get_global_queue(0, 0),指用了全局队列。
一般来说系统本身会有3个队列。
global_queue,current_queue,以及main_queue.
获取一个全局队列是接受两个参数,第一个是我分配的事物处理程序块队列优先级。分高低和默认,0为默认2为高,-2为低
[cpp] view plaincopy
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
处理完事物后,需要将结果返回给或者是刷新UI主线程,同样,和上面一样,抓取主线程,程序块操作。