Kenny 肉桂的主页

记录自己的进步


  • 首页

  • 归档

  • 标签

  • 搜索
close

ReactiveCocoa的bind方法

发表于 2017-06-17   |   分类于 ReactiveCocoa   |  

RAC的Bind方法

RAC提供了一堆可以提高开发效率的方法,比如filter,map,flattenMap等值处理方法,几乎每个方法点到底,都能看到一个叫做bind的方法.这个方法就是RAC相对底层的方法.弄明白它,对于理解RAC是非常有帮助的.

阅读全文 »

Cocoapods打包framework/静态库的注意点

发表于 2016-12-23   |   分类于 其他技术   |  

背景

关于使用Cocoapods打包静态库或者是Framework的文章,网上一搜一堆.可是当你读完,发现用处有限.它们的都是用最简单的场景串了一遍打包的流程,而整个过程中的多个坑,却根本没有提及.这就造成了一个假象,Cocoapods打包framework是简单愉快的,放弃原来的创建framework的形式吧!结果,当热情满满的开发者去用的时候,发现已经入了歧途了!

本人目前正在处理一款语音框架的工作,所以正好整理出来,希望能够对大家有帮助!

Cocoapods流程

关于如何安装Cocoapods,在此就不赘述了.如果没有Cocoapods使用经验,也不建议看这篇文章. 另外,建议将Cocoapods升级到最新版本(包括最新的beta),升级命令如下:

1
gem install cocoapods --pre

创建基本的工程结构

  1. 通过命令创建一个规范规程(这个很重要,方便你的开发与验证)

    1
    pod lib create YouFrameworkName
  2. 接下来就是一些问题,根据实际情况选择.(我这开发的OC版本)

特别说明: 一定要创建Demo Application!这个对你的开发有极大的帮助!!

具体步骤也不赘述了,给大家几篇参考文章.做完就具备了基本的结构了.主要是想一块继续深入下去.

使用CocoaPods开发并打包静态库

一步一步教你使用CocoaPods打包静态库

继续深入

有一个问题需要注意: 如果你选择打包.a的静态库,那么cocoapods是不会给你生成头文件的 ,这就意味着,其实,你只能打包framework.

按照上面的两篇文章做完,你会发现.握草,根本不够项目用啊.我项目还有三方的framework,还有三方的.a库,还想添加pch文件…

另外,关于各种路径的问题,例如:s.source_files 和s.header_dir这些,经常会被Cocoapods报错:pattern匹配不到任何文件.其实,只要记住一个原则:你的库路径(可能是线上git库也可能是本地git库)+你的指定路径 = 要找的文件路径,那么就一定没问题. 例如我的库路径是::git => '/Users/kenny/Documents/LTVoiceAssistant' 而我指定的s.source_files = 'LTVoiceAssistant/Classes/**/*.{c,h,hh,m,mm,cpp,a,pch}',它们两个拼接,正好是我各种源码文件的路径.

三方Framework和Library

在项目中,我们经常会有一些以文件形式存在的framework和.a,例如我项目中就有百度的语音识别和一个高德的.而由于某些原因,你不能用cocoapods将其管理,只能拖到项目中,cocoapods使用vendored_frameworks和vendored_libraries字段进行设置:

1
2
3
4
s.ios.vendored_libraries = 'LTVoiceAssistant/Classes/libBDVoiceRecognitionClient.a',
'LTVoiceAssistant/Classes/libBDSSpeechSynthesizer.a'

s.ios.vendored_frameworks = 'LTVoiceAssistant/Classes/*.framework'

pch

如果你不是从零开始,而是将其他项目的代码剥离打包framework的话,这个绝对会帮你大忙,因为cocoapods的验证,会检查你代码的依赖性,如果有照不到的头文件或者引入关系不对,是通不过验证的.podspec中,提供了两种方式添加pch文件.
方法一 : 使用s.prefix_header_contents用法如下:

1
s.prefix_header_contents = '#import "AHeader.h"','#import "BHeader.h"'

可以看出,简单方便.但是也是因为简单,所以一些负责的宏之类的,你用这个是没法完成的.

方法二: 添加pch文件.
首先framework工程里(不是DemoApplication中)添加pch文件.然后将文件加入到编译的source中(如果你是拷贝进来的头文件)
最后,在podspec中,添加下面的信息:

1
s.prefix_header_file = 'LTVoiceAssistant/Classes/Global/LTSpeech-prefix.pch'

在cocoapods的issue中,作者特别提到了.如果报找不到,那么是Xcode没找到,不是cocoapods,所以,确保将pch加入了编译.

xib和storyboard

如果你的代码中包含Xib和Storyboard,需要注意,这几个文件是需要添加到资源中的.

1
s.resource = 'LTVoiceAssistant/Classes/Cells/*.xib'

用__Attribute__做点好玩的事情:第一篇

发表于 2016-09-28   |   分类于 研究随笔   |  

之前写过一篇attribute((constructor))用法探究,当时是在看代码的时候,对它产生了偶遇.而这几天,越发发现这个__attribute__的强大.作为一个iOS开发者,我试着总结了一下这个在我们日常开发中的应用.

阅读全文 »

OC中的对象下标索引

发表于 2016-09-27   |   分类于 研究随笔   |  

相信对Swift有一定了解的人都知道它的下标语法.这个语法让类具备了像字典或者数组那样,利用下标获取一些东西的能力.但是,很多开发者或许不知道,OC中早就在Xcode4的时代,就已经有了这个语法.

OC中的对象下标语法

正如数组和字典, OC中的下标也分为了索引和键位.
索引下标需要声明和实现这两个方法:

1
2
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;

键索引下标需要实现这两个方法:

1
2
- (id)objectForKeyedSubscript:(id <NSCopying>)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;

这么说还是有些抽象, 现在来具体看个例子:

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@interface BlackBox : NSObject

- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;

- (id)objectForKeyedSubscript:(id <NSCopying>)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;

@end

- (id)objectAtIndexedSubscript:(NSUInteger)idx{
//做各种逻辑构造返回对象
return @(idx);
}
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx{

}

- (id)objectForKeyedSubscript:(id <NSCopying>)key{
NSString *condition = (NSString*)key;
//做各种逻辑构造返回对象
return condition;
}
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key{

}

然后使用方法:

1
2
3
BlackBox *blackBox = [BlackBox new];
id object1 = blackBox[1];
id object2 = blackBox[@"all objects where value >= 100"];

使用场景

这个最好的感觉,就是封装细节.
现在比较火的路由,用这个方法去实现就比较好,例如:

1
2
3
routes[@"black module://rootController:23"] = ^(id param){
//逻辑代码
}

和方法的对比

然后这个用方法来实现,也是okay的啊,为啥要用这个下标呢? 个人感觉,本身,我们编程工作,很大一部分的内容就是命名.而这个命名也是很容易出问题的地方.而下标的方式,让我们可以省去这个步骤.很简单的一个例子:
array[10] 和 [array objectAtIndex:10]你更愿意用哪个呢?

ProtocolKit中宏的用法分析

发表于 2016-09-26   |   分类于 研究随笔   |  

宏本身不难理解,但是往往嵌套多了,或者利用一些不常用的特性之后,会让人觉得迷惑.

ProtocolKit中的一段宏定义

在ProtocolKit中有这么一段宏的定义:

1
2
3
4
// Get container class name by counter
#define _pk_get_container_class($protocol) _pk_get_container_class_imp($protocol, __COUNTER__)
#define _pk_get_container_class_imp($protocol, $counter) _pk_get_container_class_imp_concat(__PKContainer_, $protocol, $counter)
#define _pk_get_container_class_imp_concat($a, $b, $c) $a ## $b ## _ ## $c

当时知道:

## 连字符,通常用来拼接

__COUNTER__ 计数器,一般用来后缀在变量上面,保证变量的唯一性.在程序中,每使用一次,这个数字就+1,默认是0

再看实际拼接的结果:

1
2
3
_pk_get_container_class(MyProtocol);
//相当于
__PKContainer_MyProtocol_0;

一开始的时候在想, 不就是要拼接么,为何要弄那么多层嵌套.难道是故意增加复杂度,让人觉得高深? 还有变量前面的$,是和shell中的一个意思? 表示变量?

尝试简化

遇到看不懂的东西,我喜欢先去掉,把自己知道的代码罗列出来.然后依次加上不懂得东西,看它对既有结果的改变.然后判断它的作用.
于是,我写了下面一个宏:

1
#define _kn_get_container_class(prefix,protocol) prefix##_##protocol##_##__COUNTER__

然后使用:

1
NSString *  _kn_get_container_class(__kn,MyProtocol);

可是根据编译器的警告来看,并不是我想要的结果:

20160926147485714436151.jpg

原来在##的作用下,__COUNTER__被当成了字面上的表示,并没有解析.
然后,根据作者的写法,把实现加深一层,改写为:

1
2
#define _simpleifyGetContainerClass(prefix,protocol,counter) _simple_getContainerClass_imp(prefix,protocol,counter)
#define _simple_getContainerClass_imp(a,b,c) a##b##_##c

调用

1
NSString * _simpleifyGetContainerClass(__PKContainer_,MyProtocol,__COUNTER__);

2016092614748581939059.jpg

发现结果正确.

我这种写法,需要使用者传递多个参数,也已经必须嵌套一层了.作者在此基础上加上默认的参数实现,也是很合理的.

另外可以看到,这个$符号,不加也可以,所以,个人猜想.这个和shell中使用变量不是一回事.可能加上仅仅为了阅读者明白,这个地方是个变量.若理解不正确,请您指正.

GCC中一个可变参数的宏

之前项目中,封装过打印.当时就用到了可变参数的宏:

1
2

#define GUIError(message,...) DDLogError(@"\n[ FILE ] %s \n[ METHOD ] %s\n[ LINE ] %d \n[ Message ]\n%@\n\n=======================================================\n",__FILE__,__FUNCTION__,__LINE__,[NSString stringWithFormat:message,## __VA_ARGS__])

就是在定义的时候,以...作为最后一个参数,使用的时候,__VA_ARGS__就代指这一系列可变参数.

今天想找找关于宏的资料,发现GCC的文档中,也有关于可变参数宏的说明.在此,就不赘述了,想深入了解的,可以查看文档

__attribute__((constructor))用法探究

发表于 2016-09-25   |   分类于 研究随笔   |  

今天在看ProtocolKit的源码,看到了这么一行代码.

1
__attribute__((constructor)) static void _pk_extension_inject_entry(void) {

主要造成疑惑的是 __attribute__((constructor)),以前看过关于__attribute__这个关键字的,大概还记得就是可以修饰类型,函数什么的.类似一个编译标记.但是具体用法忘记了.

阅读全文 »
12…13
桂庆

桂庆

Kenny 肉桂的主页 记录自己的进步

75 日志
17 分类
23 标签
RSS
微博
© 2013 - 2017 桂庆
由 Hexo 强力驱动
主题 - NexT.Mist