PinYin4Objc开源项目代码解读

本文为iOS新手提供了快速入门的方法,介绍了直接调试和阅读开源代码的重要性,并通过一个将汉字转换为拼音的开源项目实例,详细讲解了Objective-C的基础知识点,如assign、copy、retain的区别,单例模式的实现,以及如何使用Block等。

  对于一个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主线程,同样,和上面一样,抓取主线程,程序块操作。

转载于:https://www.cnblogs.com/xavierxia/p/4595602.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值