-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
273 lines (193 loc) · 144 KB
/
search.xml
File metadata and controls
273 lines (193 loc) · 144 KB
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[如何用reveal查看第三方app(适用于新版reveal)]]></title>
<url>%2F2018%2F05%2F17%2Fhow-to-reveal-other-apps-newest%2F</url>
<content type="text"><![CDATA[本文适用于对reveal有一定了解的,不仅仅局限查看自己开发的app,更多的是“窥视”第三方优秀的app 大致有如下几步 越狱手机一台 通过越狱iPhone上的Cydia软件,下载并安装Reveal Loader等相关软件 MacOS和该iPhone同处于一个WiFi环境下 手机越狱我有一个闲置的手机正好可以派上用场,iPhone4s(iOS 9.3.5),越狱工具用的是Phoenix4,具体方式可见iOS 9.3.5越狱 安装Cydia里相关软件 OpenSSH Cydia Substrate Reveal 安装过程我遇到的问题有: 在Cydia 里搜索不出Reveal Loader,解决方法:就点下面的“软件源”,选择“BigBoss”,选择“全部软件包”,点右边R的字母,去一个个找Reveal Loader,可以找到的,如果软件包没有任何数据的话,就先刷新下; 通过SSH连接到手机后并未在/Library目录下发现RHRevealLoader文件夹,解决方法:直接新建一个该名称的文件夹; 新版的Reveal库里未找到libReveal.dylib的文件,原因:自从Reveal更新到version 2以后,就没有dylib库了,都是RevealServer.framework,解决方案:将RevealServer.framework里的RevealServer文件直接改名为libReveal.dylib,并且将其放置手机的/Library/RHRevealLoader/目录下; 1scp /Applications/Reveal.app/Contents/SharedSupport/iOS-Libraries/RevealServer.framework/RevealServer root@192.168.X.X:/Library/RHRevealLoader/libReveal.dylib 密码默认是:alpine , 上传成功后会显示%100字样。 192.168.X.X:你的手机ip地址 到此为止,准备工作做完了,重启下Reveal 然后打开 手机打开 –> 通用 –> Reveal –> Enabled Applications –> 勾选你需要调试的app 参考链接 iOS逆向Reveal查看任意App的界面 RHRevealLoader替换library 逆向工程 - Reveal、IDA、Hopper、HTTPS抓包 等 iOS攻防 - (一)Reveal 调试别人开发的app,如微信]]></content>
</entry>
<entry>
<title><![CDATA[如何顺时针螺旋输出矩阵数组]]></title>
<url>%2F2018%2F01%2F24%2Fhow-to-clockwise-helix-output-a-matrix%2F</url>
<content type="text"><![CDATA[昨天展示了如何生成一个顺时针旋转矩阵数组(查看),今天就来逆向思维一下,如何顺时针螺旋输出一个矩阵。 123456789101112131415161718192021222324252627282930313233343536373839#include <stdio.h>int arr[5][5] = { { 1, 2, 3, 4, 5}, { 16, 17, 18, 19, 6}, { 15, 24, 25, 20, 7}, { 14, 23, 22, 21, 8}, { 13, 12, 11, 10, 9},};int main(int argc, const char * argv[]) { // 初始的区间坐标,根据初始矩阵的大小设定 int startX = 0, endX = 4; int startY = 0, endY = 4; while(startX<=endX && startY<=endY) // 循环条件 { int i; for(i=startX; i<=endX; i++){ // 输出上边的行 printf("%d ",arr[startY][i]); } startY ++; // 行的开始坐标增加 for(i=startY; i<=endY; i++){ // 输出右边的列 printf("%d ",arr[i][endX]); } endX --; // 列的结束坐标减小 for(i=endX; i>=startX; i--){ // 输出下边边的行 printf("%d ",arr[endY][i]); } endY --; // 行的结束坐标减小 for(i=endY; i>=startY; i--){ // 输出左边的列 printf("%d ",arr[i][startX]); } startX ++; // 列的开始坐标增加 } printf("\n"); return 0;} 上述5*5的螺旋矩阵运行后的结果 扩展阅读 如何生成一个旋转矩阵数组]]></content>
</entry>
<entry>
<title><![CDATA[如何生成一个旋转矩阵数组]]></title>
<url>%2F2018%2F01%2F23%2Fhow-to-generate-a-helix-matrix%2F</url>
<content type="text"><![CDATA[要求就是输入一个整数N,输出一个N*N的矩阵,该矩阵是一个顺时针螺旋矩阵。废话不多说直接码代码(C语言)。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172#include <stdio.h>#define N 100// 输入一个介于1到100之间的一个整数int scanNum(){ int num; do{ printf("Please enter a number: "); scanf("%d",&num); if (num<=0||num>N){ printf("please enter a true number.Just between 0 to 100.\n"); } }while(num<=0||num>N); return num;}// 递归,核心void funArray(int a[N][N],int num){ int i=0; int j=0; // static这个非常重要,如果没有就不能做到m,n的增加 static int n=0; static int m=1; // 输出最上面的行 for(i=n;i<num-n;i++) { a[n][i]=m++; } // 输出最右侧的列 for(j=n+1;j<num-n;j++) { a[j][num-n-1]=m++; } // 输出最底部的行 for(i=num-n-2;i>=n;i--) { a[num-n-1][i]=m++; } // 输出最左侧的列 for(j=num-n-2;j>=n+1;j--) { a[j][n]=m++; } n++; if(n<=num/2) { funArray(a,num); }}void printArray(int a[N][N],int num){ int i,j; for(i=0;i<num;i++){ for(j=0;j<num;j++){ printf("%4d",a[i][j]); } printf("\n"); }}int main(int argc, const char * argv[]) {{ int a[N][N]={0}; int num=scanNum(); funArray(a,num); printArray(a,num); return 0;} 当输入6时的运行结果: 扩展阅读 如何顺时针螺旋输出矩阵数组]]></content>
</entry>
<entry>
<title><![CDATA[Xcode 自定义文件头部注释]]></title>
<url>%2F2017%2F12%2F22%2Fxcode-header-notes%2F</url>
<content type="text"><![CDATA[如何自定义文件头部注释,想必有很多人知道,就是通过更改Xcode安装包特定目录下的配置来实现,不过这样会有一个弊端,因为工作的时候我不想把太多的个人信息保留在公司的文件上,然而牵一发而动全身,若通过这种方式更改的话,那以后凡是用该Xcode新建文件都会影响到(当然你也可以安装多个Xcode的来避免出现该问题)。好消息是现在Xcode 9 可以直接让开发者自定义文本宏,而且还可以设置生效范围。 Xcode 9 自定义文本宏基本步骤 创建一个名为IDETemplateMacros.plist的plist文件 在该plist文件里添加一个键为FILEHEADER,值为特定字符串(注释格式字符串,下面会说明) 将该plist文件放置特定的目录下(下面有说明),通过特定的目录来实现不同的生效范围 特定字符串说明类似于___FILENAME___这种字符串,是通过前后添加三个下划线_,就可以引用内置的一些宏,有关宏的介绍在这里,系统默认的注释样式等价于下面这个plist文件 12345678910111213141516<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>FILEHEADER</key> <string>// ___FILENAME___// ___PACKAGENAME___//// Created by ___FULLUSERNAME___ on ___DATE___.// // ___COPYRIGHT___// </string></dict></plist> 点击这里可以下载上述IDETemplateMacros.plist文件,通过修改该文件可以实现自定义的效果 特定的目录通过将 IDETemplateMacros.plist 文件放置到不同的目录,其影响的范围也不同 工程项目 - 单用户只对当前 Project 指定的用户(username)有影响 <ProjectName>.xcodeproj/xcuserdata/[username].xcuserdatad/IDETemplateMacros.plist 工程项目 - 多用户对当前 Project 的所有成员有影响 <ProjectName>.xcodeproj/xcshareddata/IDETemplateMacros.plist 工作区 - 单用户对指定用户的 Workspace 下的 Project 有影响 <WorkspaceName>.xcworkspace/xcuserdata/[username].xcuserdatad/IDETemplateMacros.plist 工作区 - 多用户对所有成员 Workspace 下的 Project 有影响 <WorkspaceName>.xcworkspace/xcshareddata/IDETemplateMacros.plist Xcode全局设置对 Xcode 所有创建的文件都有影响 ~/Library/Developer/Xcode/UserData/IDETemplateMacros.plist 参考链接 Text macros reference Changing Xcode Header Comment]]></content>
</entry>
<entry>
<title><![CDATA[中文域名转换(Punycode编码)]]></title>
<url>%2F2017%2F12%2F20%2Fpunycode%2F</url>
<content type="text"><![CDATA[最近女朋友闹小情绪,于是我就买了一个.我爱你后缀的国际域名,制作了一个简单但是非常有寓意的网站,最后准备通过QQ发给她。当准备发给她的时候,我发现QQ上并不支持中文域名地址,为了能够让亲爱的点一下即达目标网站,我就想到了中文域名转码,至于转成什么码就一无所知了,于是乎就在网上搜了搜,发现了PunyCode的前世与今生。 什么是PunyCodePunycode(译为:域名代码)是一种表示Unicode码和ASCII码的有限的字符集。例如:“münchen”(德国慕尼黑)会被编码为“mnchen-3ya”。Punycode的目的是在于国际化域名标签(IDNA)的框架中,使这些(多语言)的域名可以编码为ASCII。编码语法在文档RFC3492中规定 Punycode是主要用於把域名从地方语言所采用的Unicode编码转换成为可用於DNS系统的编码。Punycode可以防止所谓的IDN欺骗。 PunyCode的结构Punycode是一种用来表示国际化域名 (IDN)的编码,由域名系统支持的有限字符组合(A-Z,0-9)组成。例如,我爱你的编码为6qq986b3xl。 一个国际化域名则是在Punycode编码的基础上,在前面加上xn--。所以我爱你就变成了xn--6qq986b3xl。 如何转成PunyCode最方便的当然就是在线转码咯~ 中文域名编码在线转换]]></content>
</entry>
<entry>
<title><![CDATA[解决centos yum安装"No package nginx available."]]></title>
<url>%2F2017%2F12%2F10%2Fcentos-nginx%2F</url>
<content type="text"><![CDATA[之前在搬瓦工上面买了一个vps,平时除了作为shacksocks server之外好像也没什么其他用途,感觉资源甚是浪费,于是就想再撘一个简单网站。 安装一个轻量级的web服务器由于我买的是配置比较低的vps,因此我不得不考虑性能问题,因此我采用了大家推崇的Nginx 安装过程$ yum install nginx 谁知道抛异常了 No package nginx available. 问题原因nginx位于第三方的yum源里面,而不在centos官方yum源里面 解决方法:安装epel(Extra Packages for Enterprise Linux) 去epel网站http://fedoraproject.org/wiki/EPEL下载 我的系统是centos5.7,cpu是x86_64,所以我下载的是 1$ wget http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm 如果是centos6, 则应该下载 1wget http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm 安装epel 1rpm -ivh epel-release-5-4.noarch.rpm 再次执行 yum install nginx,则会提示安装成功了 在终端执行 $ nginx Bingo nginx服务就开启了 注:epel的安装跟centos的系统版本、cpu硬件架构有关, 查看系统版本(lsb-release -a) 查看cpu硬件架构(arch) epel它是RHEL 的 Fedora 软件仓库,为 RHEL 及衍生发行版如 CentOS、Scientific Linux 等提供高质量软件包的项目。装上了 EPEL,就像在 Fedora 上一样,可以通过 yum install package-name,随意安装软件。]]></content>
</entry>
<entry>
<title><![CDATA[CentOS下SSH远程登录服务器]]></title>
<url>%2F2017%2F12%2F09%2Fcentos-ssh%2F</url>
<content type="text"><![CDATA[检查是否已安装SSHrpm -qa |grep ssh 检查是否装了SSH包 如果未安装SSH服务,则可以通过下面命令来安装 yum -y install openssh-server 安装成功后查看SSH服务状态 service sshd status 若服务未启动,则需要手动开启。 启动SSH服务如果这时候直接在终端输入: service sshd start SSH会启动,并且默认端口是22。在这里,我们不着急开启SSH。先修改SSH默认的端口号,如下图,输入命令(注意vi后面有个空格): vi /etc/ssh/sshd_config 进入后把Port后面默认的22端口改成别的端口(如果需要更换接口的话,注意前面的#号要去掉) 然后使用命令: ss -lnt 查询22号端口是否开启,如下图所示为正常开启 连接SSH输入 ssh root@x.x.x.x x.x.x.x为某个ip地址, 然后按照提示输入密码,即可连接成功]]></content>
</entry>
<entry>
<title><![CDATA[NSObject的load和initialize方法]]></title>
<url>%2F2017%2F11%2F19%2Fload-and-initialize%2F</url>
<content type="text"><![CDATA[iOS开发中总能看到+load和+initialize的身影,网上对于这两个方法有很多解释,官方也有说明,但有些细节不够清楚,今天我们来详细扒一扒这两个方法. loadApple文档是这样描述的 12345678910111213141516171819202122232425262728293031当类(Class)或者类别(Category)加入Runtime中时(就是被引用的时候)。实现该方法,可以在加载时做一些类特有的操作。DiscussionThe load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.The order of initialization is as follows:All initializers in any framework you link to.调用所有的Framework中的初始化方法All +load methods in your image.调用所有的+load方法All C++ static initializers and C/C++ attribute(constructor) functions in your image.调用C++的静态初始化方及C/C++中的attribute(constructor)函数All initializers in frameworks that link to you.调用所有链接到目标文件的framework中的初始化方法In addition:A class’s +load method is called after all of its superclasses’ +load methods.一个类的+load方法在其父类的+load方法后调用A category +load method is called after the class’s own +load method.一个Category的+load方法在被其扩展的类的自有+load方法后调用In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes may not have run yet.在+load方法中,可以安全地向同一二进制包中的其它无关的类发送消息,但接收消息的类中的+load方法可能尚未被调用。 文档地址:https://developer.apple.com/reference/objectivec/nsobject/1418815-load?language=objc load函数调用特点如下:当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),与这个类是否被用到无关,每个类的load函数只会自动调用一次.由于load函数是系统自动加载的,因此不需要调用父类的load函数,否则父类的load函数会多次执行。 当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类 当子类未实现load方法时,不会调用父类load方法 类中的load方法执行顺序要优先于类别(Category) 当有多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序与类别在Compile Sources中出现的顺序一致) 当然当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致 initialize:Apple文档是这样描述的 123456789Initializes the class before it receives its first message.在这个类接收第一条消息之前调用。DiscussionThe runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.) The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses.Runtime在一个程序中每一个类的一个程序中发送一个初始化一次,或是从它继承的任何类中,都是在程序中发送第一条消息。(因此,当该类不使用时,该方法可能永远不会被调用。)运行时发送一个线程安全的方式初始化消息。父类的调用一定在子类之前。 文档地址:https://developer.apple.com/reference/objectivec/nsobject/1418639-initialize?language=objc initialize函数调用特点如下:initialize在类或者其子类的第一个方法被调用前调用。即使类文件被引用进项目,但是没有使用,initialize不会被调用。由于是系统自动调用,也不需要再调用 [super initialize] ,否则父类的initialize会被多次执行。假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。 父类的initialize方法会比子类先执行 当子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法. 当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法) 什么情况下使用+load由于调用load方法时的环境很不安全,我们应该尽量减少load方法的逻辑。另一个原因是load方法是线程安全的,它内部使用了锁,所以我们应该避免线程阻塞在load方法中 load很常见的一个使用场景,交换两个方法的实现 12345678910111213141516//摘自MJRefresh+ (void)load{ [self exchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)]; [self exchangeInstanceMethod1:@selector(reloadRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_reloadRowsAtIndexPaths:withRowAnimation:)]; [self exchangeInstanceMethod1:@selector(deleteRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_deleteRowsAtIndexPaths:withRowAnimation:)]; [self exchangeInstanceMethod1:@selector(insertRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_insertRowsAtIndexPaths:withRowAnimation:)]; [self exchangeInstanceMethod1:@selector(reloadSections:withRowAnimation:) method2:@selector(mj_reloadSections:withRowAnimation:)]; [self exchangeInstanceMethod1:@selector(deleteSections:withRowAnimation:) method2:@selector(mj_deleteSections:withRowAnimation:)]; [self exchangeInstanceMethod1:@selector(insertSections:withRowAnimation:) method2:@selector(mj_insertSections:withRowAnimation:)];}+ (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2{ method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));} +initializeinitialize方法主要用来对一些不方便在编译期初始化的对象进行赋值。比如NSMutableArray这种类型的实例化依赖于runtime的消息发送,所以显然无法在编译器初始化: 12345678910// In Person.m// int类型可以在编译期赋值static int someNumber = 0; static NSMutableArray *someArray;+ (void)initialize { if (self == [Person class]) { // 不方便编译期复制的对象在这里赋值 someArray = [[NSMutableArray alloc] init]; }} 总结+load方法要点当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),与这个类是否被用到无关,每个类的load函数只会自动调用一次.由于load函数是系统自动加载的,因此不需要再调用[super load],否则父类的load函数会多次执行。 1. 当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类 2. 当一个类未实现load方法时,不会调用父类load方法 3. 类中的load方法执行顺序要优先于类别(Category) 4. 当有多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序与类别在Compile Sources中出现的顺序一致) 5. 当然当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致 注意: load调用时机比较早,当load调用时,其他类可能还没加载完成,运行环境不安全. load方法是线程安全的,它使用了锁,我们应该避免线程阻塞在load方法. +initialize方法要点initialize在类或者其子类的第一个方法被调用前调用。即使类文件被引用进项目,但是没有使用,initialize不会被调用。由于是系统自动调用,也不需要显式的调用父类的initialize,否则父类的initialize会被多次执行。假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。 1. 父类的initialize方法会比子类先执行 2. 当子类不实现initialize方法,会把父类的实现继承过来调用一遍。在此之前,父类的方法会被优先调用一次 3. 当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法) 注意: 在initialize方法收到调用时,运行环境基本健全。 initialize内部也使用了锁,所以是线程安全的。但同时要避免阻塞线程,不要再使用锁]]></content>
</entry>
<entry>
<title><![CDATA[lldb命令]]></title>
<url>%2F2017%2F11%2F14%2Flldb%2F</url>
<content type="text"><![CDATA[LLDB是个开源的内置于XCode的具有REPL(read-eval-print-loop)特征的Debugger,其可以安装C++或者Python插件。 expression 命令expression命令是执行一个表达式,并将表达式返回的结果输出,是LLDB调试命令中最重要的命令,也是我们常用的 p 和 po 命令的 鼻祖。 他主要有2个功能: 执行表达式 举例:改变视图背景颜色(无需改变xcode中的项目文件代码) 输出返回值 衍生出的扩展命令 p & print & e & call 命令 po 命令oc里所有的对象都是用指针表示的,打印出来的是对象的指针,而不是对象本身,可以采用 -o 来打印对象本身 为了更加方便的时候,LLDB为 “expression -o —” 定了一个别名 :po 更多 expression 命令 可自行通过 “help expression” 命令获得 target命令大伙经常做项目都知道,iOS程序崩溃,很多都是因为 数组越界 访问野指针 适配布局语句不合法 版本系统兼容 等原因造成的,但遇到多人协同开发的项目,遇到崩溃问题,有时候往往我们不能最快的定位到项目崩溃的代码位置,因为很多代码不是我们本人写的, 我们尝试了 为 xcode 设置全局断点的方法,来获取代码崩溃的位置,但这种方法,只有60%的成功几率。 假设我们尝试了之前寻找崩溃的方法都不管用,还好我们可以使用 LLDB命令行来调试 我们运行这样一段代码 很明显是一个数组越界的语句,运行结果,程序直接crash我们看一下崩溃日志,发现看到的都是一些 地址之类的相关信息,不知道这些地址有什么作用,其实这些地址非常有用,我们分析一些 堆栈帧 ,记住找到 崩溃帧所在的前一帧的地址 当我们有一个地址,想找到具体地址对应的文件的位置的时候,可以使用 target modules lookup -a随着LLDB版本的更新,target modules 又可以简化为 image所以 target modules lookup -a 可以写为 image lookup -a 我们尝试一下这个命令 发现LLDB帮我们定位到代码崩溃在了 VIewController这个文件 textLLDBCrash 这个方法里,具体代码在 46行,对照图一中 数组越界的代码行数就是 46行,一点没错。 thread 命令thread returnDebug 阶段,有时候我们因为某些原因,不想让代码执行某个方法,或者想直接返回一个想要的值,这个时候 可以使用 thread return 扩展阅读 LLDB使用篇(上) lldb 常用命令整理 lldb与gdb命令名的对照表 The LLDB Debugger About LLDB and Xcode iOS调试-LLDB学习总结]]></content>
</entry>
<entry>
<title><![CDATA[HTTP状态码]]></title>
<url>%2F2017%2F06%2F22%2Fhttp-response-code%2F</url>
<content type="text"><![CDATA[HTTP状态码作为计算机网络的基础知识,多少还是需要了解一下的~ 常见的HTTP状态码有如下几种 200 OK 客户端请求成功 301 Moved Permanently 请求永久重定向 302 Moved Temporarily 请求临时重定向 304 Not Modified 文件未修改,可以直接使用缓存的文件。 400 Bad Request 由于客户端请求有语法错误,不能被服务器所理解。 401 Unauthorized 请求未经授权。这个状态代码必须和WWW-Authenticate报头域一起使用 403 Forbidden 服务器收到请求,但是拒绝提供服务。服务器通常会在响应正文中给出不提供服务的原因 404 Not Found 请求的资源不存在,例如,输入了错误的URL 500 Internal Server Error 服务器发生不可预期的错误,导致无法完成客户端的请求。 503 Service Unavailable 服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常。 HTTP状态码汇总 编号 名称 解释 1️⃣❌❌ 🔴🔴🔴 1xx消息 这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。由于HTTP/1.0协议中没有定义任何1xx状态码,所以除非在某些试验条件下,服务器禁止向此类客户端发送1xx响应。 这些状态码代表的响应都是信息性的,标示客户应该采取的其他行动。 100 Continue 客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。 101 Switching Protocols 服务器已经理解了客户端的请求,并将通过Upgrade消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到在Upgrade消息头中定义的那些协议。: 只有在切换新的协议更有好处的时候才应该采取类似措施。例如,切换到新的HTTP版本比旧版本更有优势,或者切换到一个实时且同步的协议以传送利用此类特性的资源。 102 Processing 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。 2️⃣❌❌ 🔴🔴🔴 2xx成功 这一类型的状态码,代表请求已成功被服务器接收、理解、并接受。 200 OK 请求已成功,请求所希望的响应头或数据体将随此响应返回。 201 Created 请求已经被实现,而且有一个新的资源已经依据请求的需要而创建,且其URI已经随Location头信息返回。假如需要的资源无法及时创建的话,应当返回’202 Accepted’。 202 Accepted 服务器已接受请求,但尚未处理。正如它可能被拒绝一样,最终该请求可能会也可能不会被执行。在异步操作的场合下,没有比发送这个状态码更方便的做法了。:返回202状态码的响应的目的是允许服务器接受其他过程的请求(例如某个每天只执行一次的基于批处理的操作),而不必让客户端一直保持与服务器的连接直到批处理操作全部完成。在接受请求处理并返回202状态码的响应应当在返回的实体中包含一些指示处理当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便用户能够估计操作是否已经完成。 203 Non-Authoritative Information 服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。当前的信息可能是原始版本的子集或者超集。例如,包含资源的元数据可能导致原始服务器知道元信息的超集。使用此状态码不是必须的,而且只有在响应不使用此状态码便会返回200 OK的情况下才是合适的。 204 No Content 服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。响应可能通过实体头部的形式,返回新的或更新后的元信息。如果存在这些头部信息,则应当与所请求的变量相呼应。 如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档。 由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。 205 Reset Content 服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,返回此状态码的响应要求请求者重置文档视图。该响应主要是被用于接受用户输入后,立即重置表单,以便用户能够轻松地开始另一次输入。 与204响应一样,该响应也被禁止包含任何消息体,且以消息头后的第一个空行结束。 206 Partial Content 服务器已经成功处理了部分GET请求。类似于FlashGet或者迅雷这类的HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。 该请求必须包含Range头信息来指示客户端希望得到的内容范围,并且可能包含If-Range来作为请求条件。 响应必须包含如下的头部域: Content-Range用以指示本次响应中返回的内容的范围;如果是Content-Type为multipart/byteranges的多段下载,则每一multipart段中都应包含Content-Range域用以指示本段的内容范围。假如响应中包含Content-Length,那么它的数值必须匹配它返回的内容范围的真实字节数。 Date ETag和/或Content-Location,假如同样的请求本应该返回200响应。 Expires, Cache-Control,和/或Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。 假如本响应请求使用了If-Range强缓存验证,那么本次响应不应该包含其他实体头;假如本响应的请求使用了If-Range弱缓存验证,那么本次响应禁止包含其他实体头;这避免了缓存的实体内容和更新了的实体头信息之间的不一致。否则,本响应就应当包含所有本应该返回200响应中应当返回的所有实体头部域。 假如ETag或Last-Modified头部不能精确匹配的话,则客户端缓存应禁止将206响应返回的内容与之前任何缓存过的内容组合在一起。 任何不支持Range以及Content-Range头的缓存都禁止缓存206响应返回的内容。 207 Multi-Status 由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。 3️⃣❌❌ 🔴🔴🔴 3xx重定向 这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的Location域中指明。当且仅当后续的请求所使用的方法是GET或者HEAD时,用户浏览器才可以在没有用户介入的情况下自动提交所需要的后续请求。客户端应当自动监测无限循环重定向(例如:A→B→C→……→A或A→A),因为这会导致服务器和客户端大量不必要的资源消耗。按照HTTP/1.0版规范的建议,浏览器不应自动访问超过5次的重定向。 300 Multiple Choices 被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。 除非这是一个HEAD请求,否则该响应应当包括一个资源特性及地址的列表的实体,以便用户或浏览器从中选择最合适的重定向地址。这个实体的格式由Content-Type定义的格式所决定。浏览器可能根据响应的格式以及浏览器自身能力,自动作出最合适的选择。当然,RFC 2616规范并没有规定这样的自动选择该如何进行。 如果服务器本身已经有了首选的回馈选择,那么在Location中应当指明这个回馈的URI;浏览器可能会将这个Location值作为自动重定向的地址。此外,除非额外指定,否则这个响应也是可缓存的。 301 Moved Permanently 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。 新的永久性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。 如果这不是一个GET或者HEAD请求,因此浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。 注意:对于某些使用HTTP/1.0协议的浏览器,当它们发送的POST请求得到了一个301响应的话,接下来的重定向请求将会变成GET方式。 302 Found 请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 新的临时性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。 如果这不是一个GET或者HEAD请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。 注意:虽然RFC 1945和RFC 2068规范不允许客户端在重定向时改变请求的方法,但是很多现存的浏览器将302响应视作为303响应,并且使用GET方式访问在Location中规定的URI,而无视原先请求的方法。状态码303和307被添加了进来,用以明确服务器期待客户端进行何种反应。 303 See Other 对应当前请求的响应可以在另一个URI上被找到,而且客户端应当采用GET的方式访问那个资源。这个方法的存在主要是为了允许由脚本激活的POST请求输出重定向到一个新的资源。这个新的URI不是原始资源的替代引用。同时,303响应禁止被缓存。当然,第二个请求(重定向)可能被缓存。 新的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。 注意:许多HTTP/1.1版以前的浏览器不能正确理解303状态。如果需要考虑与这些浏览器之间的互动,302状态码应该可以胜任,因为大多数的浏览器处理302响应时的方式恰恰就是上述规范要求客户端处理303响应时应当做的。 304 Not Modified 如果客户端发送了一个带条件的GET请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。 该响应必须包含以下的头信息: Date,除非这个服务器没有时钟。假如没有时钟的服务器也遵守这些规则,那么代理服务器以及客户端可以自行将Date字段添加到接收到的响应头中去(正如RFC 2068中规定的一样),缓存机制将会正常工作。 ETag和/或Content-Location,假如同样的请求本应返回200响应。 Expires, Cache-Control,和/或Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。 假如本响应请求使用了强缓存验证,那么本次响应不应该包含其他实体头;否则(例如,某个带条件的GET请求使用了弱缓存验证),本次响应禁止包含其他实体头;这避免了缓存了的实体内容和更新了的实体头信息之间的不一致。 假如某个304响应指明了当前某个实体没有缓存,那么缓存系统必须忽视这个响应,并且重复发送不包含限制条件的请求。 假如接收到一个要求更新某个缓存条目的304响应,那么缓存系统必须更新整个条目以反映所有在响应中被更新的字段的值。 305 Use Proxy 被请求的资源必须通过指定的代理才能被访问。Location域中将给出指定的代理所在的URI信息,接收者需要重复发送一个单独的请求,通过这个代理才能访问相应资源。只有原始服务器才能创建305响应。 注意:RFC 2068中没有明确305响应是为了重定向一个单独的请求,而且只能被原始服务器创建。忽视这些限制可能导致严重的安全后果。 306 Switch Proxy 在最新版的规范中,306状态码已经不再被使用。 307 Temporary Redirect 请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 新的临时性的URI应当在响应的Location域中返回。除非这是一个HEAD请求,否则响应的实体中应当包含指向新的URI的超链接及简短说明。因为部分浏览器不能识别307响应,因此需要添加上述必要信息以便用户能够理解并向新的URI发出访问请求。 如果这不是一个GET或者HEAD请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。 4️⃣❌❌ 🔴🔴🔴 4xx客户端错误这类的状态码代表了客户端看起来可能发生了错误,妨碍了服务器的处理。除非响应的是一个HEAD请求,否则服务器就应该返回一个解释当前错误状况的实体,以及这是临时的还是永久性的状况。这些状态码适用于任何请求方法。浏览器应当向用户显示任何包含在此类错误响应中的实体内容。如果错误发生时客户端正在传送数据,那么使用TCP的服务器实现应当仔细确保在关闭客户端与服务器之间的连接之前,客户端已经收到了包含错误信息的数据包。如果客户端在收到错误信息后继续向服务器发送数据,服务器的TCP栈将向客户端发送一个重置数据包,以清除该客户端所有还未识别的输入缓冲,以免这些数据被服务器上的应用程序读取并干扰后者。 400 Bad Request 由于包含语法错误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。 401 Unauthorized 当前请求需要用户验证。该响应必须包含一个适用于被请求资源的WWW-Authenticate信息头用以询问用户信息。客户端可以重复提交一个包含恰当的Authorization头信息的请求。如果当前请求已经包含了Authorization证书,那么401响应代表着服务器验证已经拒绝了那些证书。如果401响应包含了与前一个响应相同的身份验证询问,且浏览器已经至少尝试了一次验证,那么浏览器应当向用户展示响应中包含的实体信息,因为这个实体信息中可能包含了相关诊断信息。参见RFC 2617。 402 Payment Required 该状态码是为了将来可能的需求而预留的。 403 Forbidden 服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个HEAD请求,而且服务器希望能够讲清楚为何请求不能被执行,那么就应该在实体内描述拒绝的原因。当然服务器也可以返回一个404响应,假如它不希望让客户端获得任何信息。 404 Not Found 请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。 405 Method Not Allowed 请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个Allow头信息用以表示出当前资源能够接受的请求方法的列表。 鉴于PUT,DELETE方法会对服务器上的资源进行写操作,因而绝大部分的网页服务器都不支持或者在默认配置下不允许上述请求方法,对于此类请求均会返回405错误。 406 Not Acceptable 请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。 除非这是一个HEAD请求,否则该响应就应当返回一个包含可以让用户或者浏览器从中选择最合适的实体特性以及地址列表的实体。实体的格式由Content-Type头中定义的媒体类型决定。浏览器可以根据格式及自身能力自行作出最佳选择。但是,规范中并没有定义任何作出此类自动选择的标准。 407 Proxy Authentication Required 与401响应类似,只不过客户端必须在代理服务器上进行身份验证。代理服务器必须返回一个Proxy-Authenticate用以进行身份询问。客户端可以返回一个Proxy-Authorization信息头用以验证。参见RFC 2617。 408 Request Timeout 请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。客户端可以随时再次提交这一请求而无需进行任何更改。 409 Conflict 由于和被请求的资源的当前状态之间存在冲突,请求无法完成。这个代码只允许用在这样的情况下才能被使用:用户被认为能够解决冲突,并且会重新提交新的请求。该响应应当包含足够的信息以便用户发现冲突的源头。 冲突通常发生于对PUT请求的处理中。例如,在采用版本检查的环境下,某次PUT提交的对特定资源的修改请求所附带的版本信息与之前的某个(第三方)请求向冲突,那么此时服务器就应该返回一个409错误,告知用户请求无法完成。此时,响应实体中很可能会包含两个冲突版本之间的差异比较,以便用户重新提交归并以后的新版本。 410 Gone 被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。如果可能,拥有链接编辑功能的客户端应当在获得用户许可后删除所有指向这个地址的引用。如果服务器不知道或者无法确定这个状况是否是永久的,那么就应该使用404状态码。除非额外说明,否则这个响应是可缓存的。 410响应的目的主要是帮助网站管理员维护网站,通知用户该资源已经不再可用,并且服务器拥有者希望所有指向这个资源的远端连接也被删除。这类事件在限时、增值服务中很普遍。同样,410响应也被用于通知客户端在当前服务器站点上,原本属于某个个人的资源已经不再可用。当然,是否需要把所有永久不可用的资源标记为’410 Gone’,以及是否需要保持此标记多长时间,完全取决于服务器拥有者。 411 Length Required 服务器拒绝在没有定义Content-Length头的情况下接受请求。在添加了表明请求消息体长度的有效Content-Length头之后,客户端可以再次提交该请求。 412 Precondition Failed 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。这个状态码允许客户端在获取资源时在请求的元信息(请求头字段数据)中设置先决条件,以此避免该请求方法被应用到其希望的内容以外的资源上。 413 Request Entity Too Large 服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。此种情况下,服务器可以关闭连接以免客户端继续发送此请求。 如果这个状况是临时的,服务器应当返回一个Retry-After的响应头,以告知客户端可以在多少时间以后重新尝试。 414 Request-URI Too Long 请求的URI长度超过了服务器能够解释的长度,因此服务器拒绝对该请求提供服务。这比较少见,通常的情况包括: 本应使用POST方法的表单提交变成了GET方法,导致查询字符串(Query String)过长。 重定向URI“黑洞”,例如每次重定向把旧的URI作为新的URI的一部分,导致在若干次重定向后URI超长。 客户端正在尝试利用某些服务器中存在的安全漏洞攻击服务器。这类服务器使用固定长度的缓冲读取或操作请求的URI,当GET后的参数超过某个数值后,可能会产生缓冲区溢出,导致任意代码被执行[1]。没有此类漏洞的服务器,应当返回414状态码。 415 Unsupported Media Type 对于当前请求的方法和所请求的资源,请求中提交的实体并不是服务器中所支持的格式,因此请求被拒绝。 416 Requested Range Not Satisfiable 如果请求中包含了Range请求头,并且Range中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义If-Range请求头,那么服务器就应当返回416状态码。 假如Range使用的是字节范围,那么这种情况就是指请求指定的所有数据范围的首字节位置都超过了当前资源的长度。服务器也应当在返回416状态码的同时,包含一个Content-Range实体头,用以指明当前资源的长度。这个响应也被禁止使用multipart/byteranges作为其Content-Type。 417 Expectation Failed 在请求头Expect中指定的预期内容无法被服务器满足,或者这个服务器是一个代理服务器,它有明显的证据证明在当前路由的下一个节点上,Expect的内容无法被满足。 418 I’m a teapot 本操作码是在1998年作为IETF的传统愚人节笑话, 在RFC 2324 超文本咖啡壶控制协议中定义的,并不需要在真实的HTTP服务器中定义。 421 There are too many connections from your internet address 从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。 422 Unprocessable Entity 请求格式正确,但是由于含有语义错误,无法响应。(RFC 4918 WebDAV) 423 Locked 当前资源被锁定。(RFC 4918 WebDAV) 424 Failed Dependency 由于之前的某个请求发生的错误,导致当前请求失败,例如PROPPATCH。(RFC 4918 WebDAV) 425 Unordered Collection 在WebDav Advanced Collections草案中定义,但是未出现在《WebDAV顺序集协议》(RFC 3658)中。 426 Upgrade Required 客户端应当切换到TLS/1.0。(RFC 2817) 449 Retry With 由微软扩展,代表请求应当在执行完适当的操作后进行重试。 5️⃣❌❌ 🔴🔴🔴 5xx服务器错误 这类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生,也有可能是服务器意识到以当前的软硬件资源无法完成对请求的处理。除非这是一个HEAD请求,否则服务器应当包含一个解释当前错误状态以及这个状况是临时的还是永久的解释信息实体。浏览器应当向用户展示任何在当前响应中被包含的实体。这些状态码适用于任何响应方法。 500 Internal Server Error 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。 501 Not Implemented 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。 502 Bad Gateway 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。 503 Service Unavailable 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个Retry-After头用以标明这个延迟时间。如果没有给出这个Retry-After信息,那么客户端应当以处理500响应的方式处理它。 504 Gateway Timeout 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 注意:某些代理服务器在DNS查询超时时会返回400或者500错误 505 HTTP Version Not Supported 服务器不支持,或者拒绝支持在请求中使用的HTTP版本。这暗示着服务器不能或不愿使用与客户端相同的版本。响应中应当包含一个描述了为何版本不被支持以及服务器支持哪些协议的实体。 506 Variant Also Negotiates 由《透明内容协商协议》(RFC 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点。 507 Insufficient Storage 服务器无法存储完成请求所必须的内容。这个状况被认为是临时的。WebDAV(RFC 4918) 509 Bandwidth Limit Exceeded 服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。 510 Not Extended 获取资源所需要的策略并没有没满足。 参考链接 计算机网络—HTTP协议 HTTP状态码汇总]]></content>
</entry>
<entry>
<title><![CDATA[事件传递响应链]]></title>
<url>%2F2017%2F05%2F05%2Fevent-responder-chain%2F</url>
<content type="text"><![CDATA[我记得我刚接触触屏设备是在03、04年的时候,那个时候为了学习英语,就花了几百大洋去买了一个英语学习通,那个时候的几百大洋可比现在值钱多了。那个时候的屏幕都是电阻屏的,你点击屏幕就会发现屏幕被你“按”了一下,可以很明显的从视觉角度看到手指的力度。这个事件传递“看”起来就比较直观(可能原理也比较复杂)。如今的苹果触屏设备都是电容屏,可以支持多点触控,极大的丰富了我们十指姑娘。好吧,废话不多说了,针对iOS设备用户来说,他们操作设备的方式主要有三种:触摸屏幕、晃动设备、通过遥控设施控制设备。 事件分类 触屏事件(Touch Event) 运动事件(Motion Event) 远端控制事件(Remote-Control Event) 响应者链当发生事件响应时,必须知道由谁来响应事件。在 iOS 中,由响应者链来对事件进行响应。 所有事件响应的类都是 UIResponder 的子类,响应者链是一个由不同对象组成的层次结构,其中的每个对象将依次获得响应事件消息的机会。当发生事件时,事件首先被发送给第一响应者,第一响应者往往是事件发生的视图,也就是用户触摸屏幕的地方。事件将沿着响应者链一直向下传递,直到被接受并做出处理。一般来说,第一响应者是个视图对象或者其子类对象,当其被触摸后事件被交由它处理,如果它不处理,事件就会被传递给它的视图控制器对象 ViewController(如果存在),然后是它的父视图(superview)对象(如果存在),以此类推,直到顶层视图。接下来会沿着顶层视图(top view)到窗口(UIWindow 对象)再到程序(UIApplication 对象)。如果整个过程都没有响应这个事件,该事件就被丢弃。一般情况下,在响应者链中只要由对象处理事件,事件就停止传递。 一个典型的事件响应路线如下: First Responser --> The Window --> The Application --> nil(丢弃) 我们可以通过[responder nextResponder] 找到当前 responder 的下一个 responder,持续这个过程到最后会找到 UIApplication 对象。 通常情况下,我们在 First Responder (一般也就是用户当前触控的 View )这里就会响应请求,进入下面的事件分发机制。 事件分发第一响应者(First responder)指的是当前接受触摸的响应者对象(通常是一个 UIView 对象),即表示当前该对象正在与用户交互,它是响应者链的开端。响应者链和事件分发的使命都是找出第一响应者。 iOS 系统检测到手指触摸 (Touch) 操作时会将其打包成一个 UIEvent 对象,并放入当前活动 Application 的事件队列,单例的 UIApplication 会从事件队列中取出触摸事件并传递给单例的 UIWindow 来处理,UIWindow 对象首先会使用 hitTest:withEvent:方法寻找此次 Touch 操作初始点所在的视图(View),即需要将触摸事件传递给其处理的视图,这个过程称之为 hit-test view。 hitTest:withEvent:方法的处理流程如下: 首先调用当前视图的 pointInside:withEvent: 方法判断触摸点是否在当前视图内; 若返回 NO, 则 hitTest:withEvent: 返回 nil,若返回 YES, 则向当前视图的所有子视图 (subviews) 发送 hitTest:withEvent: 消息,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从 subviews 数组的末尾向前遍历,直到有子视图返回非空对象或者全部子视图遍历完毕; 若第一次有子视图返回非空对象,则 hitTest:withEvent: 方法返回此对象,处理结束; 如所有子视图都返回空,则 hitTest:withEvent: 方法返回自身 (self)。 一个示例性的代码实现如下: 123456789101112131415161718192021- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{ UIView *touchView = self; if ([self pointInside:point withEvent:event] && (!self.hidden) && self.userInteractionEnabled && (self.alpha >= 0.01f)) { for (UIView *subView in self.subviews) { [subview convertPoint:point fromView:self]; UIView *subTouchView = [subView hitTest:subPoint withEvent:event]; if (subTouchView) { touchView = subTouchView; break; } } }else{ touchView = nil; } return touchView;} 说明 如果最终 hit-test 没有找到第一响应者,或者第一响应者没有处理该事件,则该事件会沿着响应者链向上回溯,如果 UIWindow 实例和 UIApplication 实例都不能处理该事件,则该事件会被丢弃(这个过程即上面提到的响应值链); hitTest:withEvent: 方法将会忽略隐藏 (hidden=YES) 的视图,禁止用户操作 (userInteractionEnabled=NO) 的视图,以及 alpha 级别小于 0.01(alpha<0.01)的视图。如果一个子视图的区域超过父视图的 bound 区域(父视图的 clipsToBounds 属性为 NO,这样超过父视图 bound 区域的子视图内容也会显示),那么正常情况下对子视图在父视图之外区域的触摸操作不会被识别, 因为父视图的 pointInside:withEvent: 方法会返回 NO, 这样就不会继续向下遍历子视图了。当然,也可以重写 pointInside:withEvent: 方法来处理这种情况。 我们可以重写 hitTest:withEvent: 来达到某些特定的目的。 扩展阅读 Cocoa Touch事件处理流程–响应者链 iOS开发-事件传递响应链 iOS 点击事件传递及响应 cocoa Touch 事件处理]]></content>
</entry>
<entry>
<title><![CDATA[在写一个iOS应用之前必须做的7件事]]></title>
<url>%2F2017%2F04%2F07%2F7-Things-you-must-absolutely-do-before-writing-an-iOS-app%2F</url>
<content type="text"><![CDATA[转自CocoaChina翻译活动 英文原文:7 Things you must absolutely do before writing an iOS app 这两年,我一直在编写并发布有质量的iOS 应用。我发现大多数的开发人员有直接跳进编码应用程序的核心逻辑的倾向,因为这是乐趣所在。遵循流程开发是很无聊的。 我了解到最有效的方式是,如果你提前花些时间正确设置项目,你将会为将来节省大量的时间。如果你是一位独立开发者,你可能意识不到下面提到的这些步骤的重要性。大多数优秀的应用程序都由团队开发,如果遵循以下步骤,肯定能帮你减少挫败感并提升应用质量。 1.为工程设置编码风格规范编码风格规范指的是在使用特定语言写代码之前要明确遵守的风格和惯例,它包括类似于该使用tab键还是空格键,如何命名变量以及特定语言本身的约定俗成(像swift语言中是否该使用Classes还是Structs)。 编码规范本身没有孰对孰错。在项目开始前,你可以设置自己的编码风格,但是必须保证同组的人遵守相同的规范。编码规范能够保证代码更加统一和更易于阅读。 一些公司已经开源了Objective-C和Swift语言的编码规范。 Ray Wenderlich/Swift Style Guide GitHub/swift-style-guide NYTimes/objective-c-style-guide 2.在写代码之前确定应用的架构在写代码之前,确定应用架构是非常重要的。一个好的架构可以提升应用的可测试性,更加易于理解并且能降低维护成本。你可以使用传统的MVC架构,或者使用更加流行的MVVM或VIPER架构,这里提供了大量的资源来介绍这些架构。 iOS Architecture Patterns(中文版:iOS 架构模式–解密 MVC,MVP,MVVM以及VIPER架构) Issue 13 Architecture *objc.io Modern application architectures (Reactive programming, MVVM and beyond) 3.设定应用的目录结构为了使数以百计的源代码文件至始至终保存在相同的目录中,最好是根据项目的架构制定目录结构,例如,你可以使用以下的目录结构: 首先,在Xcode的Project Navigator中工程名称分组下面,以group的形式创建它们(小黄色的文件夹),然后,通过打开Xcode右边的File Inspector,为每个创建的group链接到真实的项目路径下对应的目录,点击File Inspector中小的灰色的文件夹icon,在工程目录下创建对应group名称的子目录。 这个看起来是件小的事情,却可以使你的项目更加有条理且易于理解。 了解更多目录结构可以参考以下资源: futurice/ios-good-practices 4.项目依赖管理你当然会在项目中使用第三方库,在项目中,你可以有三种方式可以管理项目依赖。 CocoaPodsCocoaPods是适用于Swift和Objective-C Cocoa项目的依赖管理库,它有将近10000个开源库,可以优雅地帮你管理项目的规模。它是最有效的方式做依赖管理,就像Ruby中的Gems。 Youtube上有一个google开发者创建的滑稽的视频(地址,需翻墙)来解释为什么必须在项目中使用CocoaPods。 Github Submodules你也可以使用Github Submodules管理你的项目依赖作为子库,相比CocoaPods,Github Submodules的优点在于它是sub-repos- 这不仅意味着git和git GUIs能够隐式识别他们,并且也可以获得更多支持,同时意味着你的工程依赖能够更加紧密的联系到他们的git仓库,而Cocoapods则不能。 submodules的问题是:你的工程不拥有你依赖库的源代码,仅仅是拥有一个引用到submodule的仓库。大多数情况你控制不了这些代码仓库。 Carthage Carthage被认为是往Cocoa应用中添加框架的最简单的方法。Carthage使用xcodebuild编译framework二进制,但是把集成的任务留给了用户。CocoaPods的目的是对用户简单,但是Carthage对用户来说是灵活的、不干涉的。 不幸的是,Carthage的最大的缺点是—-只支持iOS8及以后版本。 这三个当中,我最常用并且我个人最喜欢的是CocoaPods,因为它设置超级简单,并且提供了数以千计的第三方库供你访问。 5.为应用设置合适的Scheme当你点击了Run、Test、Profile、Analyze或者Archive 操作后,Schemes告诉Xcode什么会发生。通常,每个操作对应一个target和一个编译配置。你也可以传递启动参数,比如应用运行的语言(测试本地化很有用)或者debug时设置一些判断的标识位。 建议Scheme的命名规则采用MyApp () [Environment]: 你也可以使用Target制作不同的发布、测试以及开发来编译程序,如以下描述: How to Use Xcode Targets to Manage Development and Production Builds 6.设置合适的Certificates和Provisioning Profiles在测试和发布应用过程中,这个是开发者最头疼且重要的步骤。证书对代码签名来说是必须的,你可以在真机上运行应用程序。 有两种类型的证书: 开发证书:每个团队的开发者都有自己的证书,需要请求生成。Xcode会为你做这些,但是最好不要点击“Fix issue”按钮,并且能够理解点击这个按钮会真正执行什么。开发证书是发布应用的开发版本到设备上。 发布证书:可以有多个,但是最好保持一个公司一个发布证书,通过内部渠道分享相关的秘钥。发布应用到App Store时需要这个证书,或者是公司内部的企业级应用分发。 Provisioning Profiles 可能是系统中最容易引起混淆的部分了,如果你访问苹果开发者网站,你会注意到你可以创建两种类型的Provisioning Profiles(开发和发布)。Provisioning Profiles是“以这个证书的私钥作为签名的应用可以在这些设备上正常运行: https://www.quora.com/What-are-the-differences-between-certificates-provisioning-profiles-and-identifiers 你可以阅读更多相关资源: Maintaining Your Signing Identities and Certificates What is a Provisioning Profile? Part 2 7.设置应用持续集成和交付过程建立一个持续集成和交付过程已成为关键,因为现在它可以帮助您在开发早期发现bug和节省大量的开发人员的时间。 持续集成 (CI) 是一种开发实践,要求开发人员一天将代码同步到共享存储库几次。每次提交都会用自动脚本进行验证,可以使团队尽早的发现问题。 很多工具可以帮你做iOS应用程序的持续集成,比如 Xcode Server、Jenkins和Travis CI。 About Continuous Integration in Xcode Travis-CI:Test and Deploy with Confidence iOS CI with Jenkins 持续交付 (CD) 是一个软件工程的方式,可以使团队在短周期内开发软件,确保软件可以在任何时间可靠地发布。它旨在更快、更频繁地构建、测试和发布的软件。 为什么使用持续交付? 可以在准备应用提交、上传截图以及发布应用上节省数天时间。 如果在同事休假期间,但你发现一个严重问题需要修复并发布怎么办?在发布更新版本时不需要依赖某一个人。 通过更频繁和小版本的更新,提高软件质量和反应时间。 虽然有大量的工具供持续交付,我个人最喜欢的是Fastlane。它非常容易安装,并提供了一些强大的功能,可以使你整个的编译和发布过程自动化。 Fastlane]]></content>
</entry>
<entry>
<title><![CDATA[浅谈加密与解密]]></title>
<url>%2F2017%2F01%2F20%2Fencryption-and-decryption%2F</url>
<content type="text"><![CDATA[这几个概念在金融电子支付领域用得比较多,我忽然觉得把它们串起来一起讲,层层引入,可能更好理解一些。希望能以最简单朴实的方式讲明白他们之间的关系。 非对称算法关于非对称算法,你只要知道下面这些就行了,密钥是一对,一个叫公钥,一个叫私钥,前者公开,后者保密。假设你有一对公私钥,给你一串数据,你可以用私钥加密,然后把密文和公钥都放出去,别人可以用这个公钥解密。同样反过来,别人也可以用这个公钥加密一串数据,你用对应的私钥解密。可以用下图来表示: 散列散列也叫哈希,学过数据结构的人对这个概念都不陌生。简单来讲,给你一串数据A,这个数据可以很长,你通过一个算法把它们转变成一个很短的固定长度(不管源串有多长)的另一串数据B。这个过程就叫散列,数据B叫数据A的散列值(或者叫哈希值,或者叫摘要)。 再深入一些。大部分情况下,A和B是一一对应的(这也是我们希望的),也就是说,如果我还有个A1,那么它的散列值B1和B不会相等。但是理想丰满,现实让人反感,B1有可能和B相等。这种现象有个学名叫”碰撞”,增加散列值的位数是防碰撞的一个方法,因为很自然位数越长,完全相同的概率就越小。目前认为超过128位的散列值都能很好的防碰撞。后面我们讲到签名时,假定是没有碰撞的。 最后再补充一点,散列具有不可逆性,也就是你没法从B还原回A,即使散列算法是公开的。 数字签名 生活中我们用签名代表自己的身份,比如领导签署一个文件,大家看到这个签名,就确认是这个领导签的,就代表他本人。签名只占用很小的信息(一般是两个字或三个字,日本人的可能长一些),却能表示你整个人的信息,这种思想确实意义很大,我们把它用在电子化的签名过程,也就是数字签名。 数字签名的过程是这样的,比如小明有一串数据A要发给小红,小明先用散列生成一个A的摘要B,然后把B用一个私钥加密后附在A的后面发给小红,小红有公钥(因为是公开的),她先用这个公钥解密A后面的数据得到B,然后自己把A散列一些算出一个B1, 比较B1和B如果相等,首先能说明数据是小明发过来的,因为只有小明才有私钥,其次能说明A在传输过程中没有被改过,因为如果修改过,散列值肯定不相等。 可能有有会有问题,数据A似乎没有加密?,确实是这样,因为这个不是小红所关心的,小红只关心这个数据是不是小明发的,数据的内容没那么重要。其实也不难理解,就跟你去超市刷卡购物一样,小票需要你签字作为对账的凭证,人们只关心这个签名是不是你本人的,对于小票的内容没这么关心。 证书仔细想想上面的验证签名的过程,似乎天衣无缝。但其实有个问题,小红的公钥是哪来的?有人说这个不是公开的吗,随便哪都可以,可以是问别人要的,可以是网上找的。其实不然,验签的前提,是小红已经假设她手上的公钥和小明的私钥是一对的。如果小王生成一对非法的公私钥对,然后给小红公钥,给她说这是小明的,就会产生问题了。所以小红要有明确的途径确认她的公钥是合法的。 打个比方,一个人站在你面前,你没法判断他是好人还是坏人,但是如果法院告诉你这个人是杀人犯,你肯定会选择相信,因为法院是权威机构。同样的,对于公钥这样的”公开的敏感信息”,也需要一个权威机构来认定。这个机构叫CA(certification authority)。这样小红只要是从CA拿的公钥,就可以认为它是合法的了。 CA一般不会直接下发公钥,它通常把公钥信息和一些附加信息(比如公钥产生的日期,有效期等)一起按照一定的格式组织起来下发,这种组织起来的数据就叫做证书。证书的作用就是它有一定的格式,这个格式还是个标准,全世界都用它,这样就很方便传播。目前用得比较多的证书就是著名的x.509。 扩展资料 非对称算法,散列(Hash)以及证书的那些事 数字签名和加密的区别 RSA的公钥和私钥到底哪个才是用来加密和哪个用来解密? 加密和签名的区别?]]></content>
</entry>
<entry>
<title><![CDATA[Mac 在线视频下载]]></title>
<url>%2F2016%2F12%2F16%2Fhow-to-download-online-video-with-mac%2F</url>
<content type="text"><![CDATA[不求最全,只求最简 介绍一个强大的命令行下载工具安装you-get1$ brew install you-get 什么?返回了:-bash: brew: command not found 这么强大的Homebrew 你以前居然没有安装?那就首先安装Homebrew吧!安装成功后再安装you-get 安装Homebrew:(以前已经安装过的,可以跳过)1$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 使用you-get下载在线视频成功安装好you-get后,通过下面命令即可下载对应网站的视频, 1$ you-get -o ~/Downloads online_video_url 使用 -o 设定路径 (小写的字母o) 比如优酷上的一个交通安全日宣传视频对应的地址是:http://v.youku.com/v_show/id_XMTg1MDU3MDQxNg==.html?from=s1.8-1-1.2&spm=a2h0k.8191407.0.0,想下载该视频,则输入下面的样式即可下载成功至~/Downloads该目录下。1$ you-get -o ~/Downloads http://v.youku.com/v_show/id_XMTg1MDU3MDQxNg==.html?from=s1.8-1-1.2&spm=a2h0k.8191407.0.0 BINGO!是不是觉得很简单~ 扩展资料 Homebrew官网 you-get 中文说明]]></content>
</entry>
<entry>
<title><![CDATA[两步让你的网站支持简体繁体切换]]></title>
<url>%2F2016%2F10%2F11%2Fhow-set-Chinese-Simplified-switch-to-Chinese-Traditional%2F</url>
<content type="text"><![CDATA[切换的基本原理,就是首先建立一个简体字与繁体字相对应的一个映射表,然后遍历整个界面,把相应的简体字或者是繁体字映射为对应的字体即可。 不少网站为了更好地照顾不同用户使用简体/繁体的阅读习惯,会提供简体繁体两种版本字体切换,提高用户体验。例如hexo虽然作者是来自台湾的,但是hexo的官网不仅提供了适合台湾同胞阅读的繁体中文版,还提供了适合我们内地同胞阅读的简体中文版,照顾了我们这些习惯使用简体读写的大陆用户,hexo得到了不少内地忠实粉丝追捧。同样,我们也可以提供繁体版来照顾那些使用繁体字的台湾、香港同胞阅读习惯。 具体实现: 首先,点击这里右键另存下载简繁字体切换所需的tw_cn.js文件,上传到自己的网站空间。 其次,修改模板,在你想要显示简繁转换按钮的地方加上下面一段代码。我就直接在footer.swig(我的hexo主题是NexT.Mist,里面布局文件的格式是.swig,其他主题的后缀名可能不相同,找到对应的文件名即可)里添加了如下代码: 123456789101112131415<div class="translate-style">繁/简:<a id="translateLink" href="javascript:translatePage();">繁體</a></div><script type="text/javascript" src="http://www.qingboshare.com/js/src/tw_cn.js"></script><script type="text/javascript">var defaultEncoding = 2; //网站编写字体是否繁体,1-繁体,2-简体var translateDelay = 0; //延迟时间,若不在前, 要设定延迟翻译时间, 如100表示100ms,默认为0var cookieDomain = "http://www.qingboshare.com/"; //Cookie地址, 一定要设定, 通常为你的网址var msgToTraditionalChinese = "繁體"; //此处可以更改为你想要显示的文字var msgToSimplifiedChinese = "简体"; //同上,但两处均不建议更改var translateButtonId = "translateLink"; //默认互换idtranslateInitilization();</script> 这就基本完成了简体繁体切换功能,不管你是hexo,jelly,Octopress等静态博客,还是wordpress,typecho,emlog,Z-Blog等动态的,都可以用上。具体的演示效果可以点击我博客底部简体中文切换字体,至于简体繁体切换按钮嘛,文字和样式可以按个人喜好自行更改。这是一篇来源于三步让你的网站支持简体繁体切换 的文章,由于原博客现在展示不了切换效果,我优化了一下文章并添加了切换效果。]]></content>
</entry>
<entry>
<title><![CDATA[Xcode 调试技巧]]></title>
<url>%2F2016%2F08%2F17%2Fxcode-debugging%2F</url>
<content type="text"><![CDATA[从事iOS开发也有好些年了,从最初的不懂得如何调试程序,到后来慢慢学会了用NSLog来调试,再到如今掌握了一些调试技巧,明显感觉到开发效率在提升。现在我就谈一谈Xcode的调试技巧,关于调试技巧,也就是如何使用断点来调试,为了让大家对断点有进一步的认识,我就把断点分为了下面几个类别: 基本断点这种是最常用的断点,也是最容易设置。左键点击一下就可以设置。 编辑断点对基本断点进行编辑,可以生成条件断点、日志信息断点、发声断点等 点击后会弹出如下内容 其中,Condition: 返回一个布尔值,只有在布尔值为真的情况下断点才会触发 Ignore:忽略前n次断点,直到第n+1次遇到断点才触发 Action:断点触发时,Xcode执行的操作,共有六种类型,如下图所示: AppleScript:用于执行脚本,如display dialog “SwiftGG”弹出对话框。 Capture GPU Frame:捕获在断点处 GPU 当前绘制的帧图,用于 OpenGL ES应用的调试。 Debugger Command:相当于在控制台中输入的 lldb 调试命令。 Log Message:将自定义格式、内容的信息输出到控制台,常用的占位符有:%H(断点第几次触发),%B(断点所在的方法的名字)和@expr@(输出expr的值)。 Shell Command:接收命令文件以及相应的参数列表。 Xcode会异步执行 Shell Command。勾选 “Wait until done”表示等待 Shell 命令执行结束后再执行调试工作。 Sound:触发断点的同时播放声音 Automatically continue after evaluating actions:勾选这个选项后,断点不会中断程序运行。 异常断点(Exception Breakpoint)异常断点是代码出现问题导致编译器抛出异常时触发的断点。它在断点导航器中设置。点击+号,选择Exception Breakpoint选项。如下图所示 点击添加后会出现下面的小框 Exception:选项可以让你选择响应Objective-C对象抛出的异常,也可以选择响应C++对象抛出的异常。 Break:则是选择断点所接收的异常,是接收“Throw”语句抛出的异常还是Catch语句的。 由于有一些异常的出现,是在不满足某些特定条件下而导致的,比如说在复杂循环中数组越界,这个时候往往不容易根据异常信息确定错误的出处,这个时候设置异常断点便能发挥作用。 符号断点(Symbolic Breakpoint)在断点导航器界面,点击+号,选择Add Symbolic Breakpoint选项 然后会弹出下面所示的对话框 Symbol:用来设置当前断点作用域所能识别的方法,这里面既可以是自定义的方法,也可以是系统的API方法。(注意必须表明是类方法还是成员方法) 例如:在 Symbol 一栏输入 viewDidLoad。这样一来,在程序中所有的 viewDidLoad 方法被调用时都会触发断点。 当然,我们也可以仅仅为特定的某个类的方法添加断点。在 Symbol 一栏输入 [ClassName viewDidLoad] (Objective-C) 或 ClassName.viewDidLoad (Swift) 即可。 监控断点我们调试程序的大部分时候都是为了监控某个变量的变化,在代码中变量出现的地方添加断点不仅累而且还可能漏掉,事后还得一个一个删掉,实在很累。 我们可以通过为变量添加监控断点来简单地做到这一点。找到变量第一次出现的地方,添加一个普通断点,进入 debug 模式后在 Variables View 中右键变量,选择 Watch 变量名。这样,每一次该变量被改变都会触发断点告知我们。 参考链接 WWDC 2013 Debugging with Xcode SwiftGG 交流分享:Xcode使用技巧 Xcode之断点调试详解 Xcode断点调试技巧 Xcode 的正确打开方式——Debugging]]></content>
</entry>
<entry>
<title><![CDATA[操作系统中的栈和堆]]></title>
<url>%2F2016%2F07%2F27%2Fstack-and-heap-in-os%2F</url>
<content type="text"><![CDATA[注:这里所说的堆和栈与数据结构中的堆和栈不是一回事。 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值、局部变量的值等。由编译器自动创建与释放。其操作方式类似于数据结构中的栈,即后进先出、先进后出的原则。 例如:在函数中申明一个局部变量int b;系统自动在栈中为b开辟空间。 堆区(heap):一般由程序员申请并指明大小,最终也由程序员释放。如果程序员不释放,程序结束时可能会由OS回收。对于堆区的管理是采用链表式管理的,操作系统有一个记录空闲内存地址的链表,当接收到程序分配内存的申请时,操作系统就会遍历该链表,遍历到一个记录的内存地址大于申请内存的链表节点,并将该节点从该链表中删除,然后将该节点记录的内存地址分配给程序。 例如:在C中malloc函数 12char p1; p1 = (char )malloc(10); 但是p1本身是在栈中的。 链表:是一种常见的基础数据结构,一般分为单向链表、双向链表、循环链表。以下为单向链表的结构图: 单向链表是链表中最简单的一种,它包含两个区域,一个信息域和一个指针域。信息域保存或显示关于节点的信息,指针域储存下一个节点的地址。 上述的空闲内存地址链表的信息域保存的就是空闲内存的地址。 全局区/静态区:顾名思义,全局变量和静态变量存储在这个区域。只不过初始化的全局变量和静态变量存储在一块,未初始化的全局变量和静态变量存储在一块。程序结束后由系统释放。 文字常量区:这个区域主要存储字符串常量。程序结束后由系统释放。 程序代码区:这个区域主要存放函数体的二进制代码。 下面举一个例子: 1234567891011121314//main.cppint a = 0; // 全局初始化区char *p1; // 全局未初始化区main { int b; // 栈 char s[] = "abc"; // 栈 char *p2; // 栈 char *p3 = "123456"; // 123456\0在常量区,p3在栈上 static int c =0; // 全局静态初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); // 分配得来的10和20字节的区域就在堆区 strcpy(p1, "123456"); // 123456\0在常量区,这个函数的作用是将"123456" 这串字符串复制一份放在p1申请的10个字节的堆区域中。 // p3指向的"123456"与这里的"123456"可能会被编译器优化成一个地址。}]]></content>
</entry>
<entry>
<title><![CDATA[Objective-C中的Block]]></title>
<url>%2F2016%2F07%2F26%2Fblock-of-objective-c%2F</url>
<content type="text"><![CDATA[block介绍 Block是iOS4.0之后新增的一种语法结构,也称为“闭包”。 Block是一个匿名的函数代码块,此代码块可以作为参数传递给其他对象。 可以把block当做Objective-C的匿名函数,block是OC中的一种数据类型,^是block的特有标记。 block原型 (返回类型)(^block名称)(参数类型)=^(参数列表){代码实现}; 1NSString * ( ^ myBlock )( int ); 上面的代码声明了一个block(^)原型,名字叫做myBlock,包含一个int型的参数,返回值为NSString类型的指针。block的定义: 1234myBlock = ^( int paramA ){ return [NSString stringWithFormat:@"The number is: %i", paramA];}; 上面的代码中,将一个函数体赋值给了myBlock变量,其接收一个名为paramA的参数,返回一个NSString对象. 注意:一定不要忘记block后面的分号。 定义好block后,就可以像使用标准函数一样使用它了: myBlock(6); 由于block数据类型的语法会降低整个代码的阅读性,所以常使用typedef来定义block类型。例如,下面的代码创建了GetPersonEducationInfo和GetPersonFamilyInfo两个新类型,这样我们就可以在下面的方法中使用更加有语义的数据类型。 1234567// Person.htypedef NSString * (^GetPersonEducationInfo)(NSString *);typedef NSString * (^GetPersonFamilyInfo)(NSString *);@interface Person : NSObject- (NSString *)getPersonInfoWithEducation:(GetPersonEducationInfo)educationInfo andFamily:(GetPersonFamilyInfo)familyInfo;@end 我们用一张图来总结一下block的结构: block数据结构定义上图这个结构是在栈中的结构,我们来看看对应的结构体定义: 123456789101112131415struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *);};struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables. */}; 从上面代码看出,Block_layout就是对block结构体的定义: isa指针:指向表明该block类型的类。 flags:按bit位表示一些block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等。 reserved:保留变量,我的理解是表示block内部的变量数。 invoke:函数指针,指向具体的block实现的函数调用地址。 descriptor:block的附加描述信息,比如保留变量数、block的大小、进行copy或dispose的辅助函数指针。 variables:因为block有闭包性,所以可以访问block外部的局部变量。这些variables就是复制到结构体中的外部局部变量或变量的地址。 block使用 作为一个局部变量 1returnType (^blockName)(parameterTypes) = ^returnType(parameters) {...}; 作为属性 1@property (nonatomic, copy, nullability) returnType (^blockName)(parameterTypes); 作为一个方法参数 1- (void)someMethodThatTakesABlock:(returnType (^nullability)(parameterTypes))blockName; 作为参数的方法调用 1[someObject someMethodThatTakesABlock:^returnType (parameters) {...}]; 作为一个typedef 12typedef returnType (^TypeName)(parameterTypes);TypeName blockName = ^returnType(parameters) {...}; 使用block应当注意的问题 Block访问外部变量 block内部可以访问外部的变量,block默认是将其复制到其数据结构中来实现访问的。 默认情况下,block内部不能修改外面的局部变量,因为通过block进行闭包的变量是const的。 给局部变量加上__block关键字,这个局部变量就可以在block内部修改,block是复制其引用地址来实现访问的。 Block作为属性应该用copy修饰 当用weak、assign修饰block属性时,block访问外部变量,此时block的类型是栈block。保存在栈中的block,当block所在函数\方法返回\结束,该block就会被销毁。在其他方法内部调用访问该block,就会引发野指针错误EXC_BAD_ACCESS。 当用copy、strong修饰block属性时,block访问外部变量,此时block的类型是堆block。保存在堆中的block,当引用计数器为0时被销毁,该类型block是由栈类型的block从栈中复制到堆中形成的,因此可以在其他方法内部调用该block。在ARC下,strong和copy都可以用来修饰block,但是建议修饰block属性使用copy。 参考链接 How Do I Declare A Block in Objective-C? 谈Objective-C block的实现 Block的使用]]></content>
</entry>
<entry>
<title><![CDATA[如何用Python的filter函数求素数]]></title>
<url>%2F2016%2F07%2F11%2Fuse-filter-method-export-prime-number-in-python%2F</url>
<content type="text"><![CDATA[算法介绍:计算素数的一个方法是埃氏筛法,它的算法理解起来非常简单: 首先,列出从2开始的所有自然数,构造一个序列: 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, … 取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉: 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, … 取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉: 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, … 取新序列的第一个数5,然后用5把序列的5的倍数筛掉: 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, … 不断筛下去,就可以得到所有的素数。 python 算法 首先可以先构造一个从3开始的奇数序列: 12345def _odd_iter(): n = 1 while True: n = n + 2 yield n 注意这是一个生成器,并且是一个无限序列。为什么要构造从3开始的奇数数列,而不是上面原理中的从2开始的所有自然数数列?因为除了2的偶数数列不可能是素数。 然后定义一个筛选函数: 12def _not_divisible(n): return lambda x: x % n > 0 最后,定义一个生成器,不断返回下一个素数: 1234567def primes(): yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一个数 yield n it = filter(_not_divisible(n), it) # 构造新序列 这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列。 由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件: 123456# 打印1000以内的素数:for n in primes(): if n < 1000: print(n) else: break 注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁。 示例Python源码下载 扩展资料 廖雪峰Python教程–生成器:generator 廖雪峰Python教程–迭代器:Iterator 廖雪峰Python教程–匿名函数:lambda 廖雪峰Python教程–筛选函数:filter]]></content>
</entry>
<entry>
<title><![CDATA[Python中列表,元组,字符串如何相互转换]]></title>
<url>%2F2016%2F07%2F07%2FHow-to-convert-type-in-list-tuple-and-str%2F</url>
<content type="text"><![CDATA[Python中列表,元组,字符串相互转换可以使用以下几个函数: list() tuple() join() str() 1234567891011121314s = "abcdefg"print(list(s))#['a', 'b', 'c', 'd', 'e', 'f', 'g']print(tuple(s))#('a', 'b', 'c', 'd', 'e', 'f', 'g')print(list(tuple(s)))#['a', 'b', 'c', 'd', 'e', 'f', 'g']print(tuple(list(s)))#('a', 'b', 'c', 'd', 'e', 'f', 'g')print("".join(list(s)))#abcdefgprint("".join(tuple(s)))#abcdefg]]></content>
</entry>
<entry>
<title><![CDATA[xcode 常用插件汇总]]></title>
<url>%2F2016%2F06%2F08%2Fxcode-plugin-part%2F</url>
<content type="text"><![CDATA[ActivatePowerMode : ActivatePowerMode is a plugin for Xcode. This plugin will make your code powerful. Backlight-for-XCode : Highlights the current editing line in Xcode Xcode-CComment : Xcode plugin for C Style Comment(uncomment) /**/ Shortcuts cocoapods-xcode-plugin : Dependency management helper for your CocoaPods, right in Xcode DXXcodeConsoleUnicodePlugin : 转换Xcode控制台中一些不可阅读的字符,比如 \u22AD 这种 ESJsonFormat-Xcode : 将JSON格式化输出为模型的属性 FuzzyAutocompletePlugin : A Xcode 5+ plugin that adds more flexible autocompletion rather than just prefix-matching.(笔者注:最新版的Xcode默认有该功能了) HOStringSense-for-Xcode : Plugin for Xcode to make working with strings less “escaped” IndentComments : IndentComments is a plugin for Xcode. This plugin will let your comments indent. injectionforxcode : Runtime Code Injection for Objective-C & Swift KSImageNamed-Xcode : Xcode plug-in that provides autocomplete for imageNamed: calls Peckham : Add #import-s from anywhere in the code. SCXcodeSwitchExpander : Xcode plugin that enables switch cases autocompletion VVDocumenter-Xcode : Xcode plug-in which helps you write documentation comment easier, for both Objective-C and Swift. XAlign : An amazing Xcode plugin to align regular code. it can align Xnything in any way you want. http://qfi.sh/XAlign/ ZLGotoSandboxPlugin : You can quickly enter the iOS simulator Xcode plugin!]]></content>
</entry>
<entry>
<title><![CDATA[抓包神器 -- Charles]]></title>
<url>%2F2016%2F06%2F07%2Fmac-software-charles%2F</url>
<content type="text"><![CDATA[Charles主要的功能包括: 支持SSL代理。可以截取分析SSL的请求。 支持流量控制。可以模拟慢速网络以及等待时间(latency)较长的请求。 支持AJAX调试。可以自动将json或xml数据格式化,方便查看。 支持AMF调试。可以将Flash Remoting 或 Flex Remoting信息格式化,方便查看。 支持重发网络请求,方便后端调试。 支持修改网络请求参数。 支持网络请求的截获并动态修改。 检查HTML,CSS和RSS内容是否符合W3C标准。 官方下载地址 破解版-百度云盘地址(密码: bv4w) 如果是抓取http数据,成功安装好后设置完代理端口号(端口号随便设置,不过一般都是8888),开启代理即可抓包,如果还是不会使用可以参考下方的扩展资料1:唐巧大神以前写过的一篇文章。 https抓包附上官网说明: LEGACY SSL PROXYING Version of Charles prior to v3.10 used a single SSL Root Certificate. You can still download the legacy certificate bundle here or the certificate itself here (for installing on mobile devices). Note that these certificates will not work on Charles v3.10. If you are running Charles v3.10 or later, please go to Charles and consult the SSL Proxying submenu in the Help menu, for instructions on installing your new Charles Root Certificate. You can also check the documentation on SSL Certificates for instructions for installing your Charles Root Certificate on various devices. 简言之: 手机端操作: 如果charles是v3.10之前的版本,则SSL证书从这个地址下载: http://www.charlesproxy.com/documentation/using-charles/ssl-certificates/ 如果是v3.10或者之后的版本(包括3.10)则SSL证书相关信息详情在这个页面: http://www.charlesproxy.com/documentation/using-charles/ssl-certificates/ 电脑端操作: 在Charles的工具栏上点击设置按钮,选择Proxy Settings…切换到SSL选项卡,选中Enable SSL Proxying。(新版本的操作:Proxy -> SSL Proxying Settings 切换到SSL选项卡,选中Enable SSL Proxying 即可) SSL选项卡的Locations里填写要抓包的域名和端口,点击Add按钮,在弹出的表单中Host填写域名。比如填www.google.com,Port填443 扩展资料 iOS开发工具——网络封包分析工具Charles charles官方文档 Charles 从入门到精通]]></content>
</entry>
<entry>
<title><![CDATA[Method Swizzling]]></title>
<url>%2F2016%2F05%2F24%2Fmethod-swizzling%2F</url>
<content type="text"><![CDATA[Method Swizzling是改变一个selector的实际实现的技术。通过这一技术,我们可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现。 例如,我们想跟踪在程序中每一个view controller展示给用户的次数:当然,我们可以在每个view controller的viewDidAppear中添加跟踪代码;但是这太过麻烦,需要在每个view controller中写重复的代码。创建一个子类可能是一种实现方式,但需要同时创建UIViewController, UITableViewController, UINavigationController及其它UIKit中view controller的子类,这同样会产生许多重复的代码。 幸运的是,还有另外一种方法,我们可以在类别中使用 Method Swizzling,代码如下所示: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546#import <objc/runtime.h>@implementation UIViewController (Tracking)+ (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(viewWillAppear:); SEL swizzledSelector = @selector(xxx_viewWillAppear:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); // When swizzling a class method, use the following: // Class class = object_getClass((id)self); // ... // Method originalMethod = class_getClassMethod(class, originalSelector); // Method swizzledMethod = class_getClassMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } });}#pragma mark - Method Swizzling- (void)xxx_viewWillAppear:(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", self);}@end 在这里,我们通过method swizzling修改了UIViewController的@selector(viewWillAppear:)对应的函数指针,使其实现指向了我们自定义的xxx_viewWillAppear的实现。这样,当UIViewController及其子类的对象调用viewWillAppear时,都会打印一条日志信息。 上面的例子很好地展示了使用method swizzling来一个类中注入一些我们新的操作。当然,还有许多场景可以使用method swizzling,在此不多举例。在此我们说说使用method swizzling需要注意的一些问题: Swizzling应该总是在+load中执行在Objective-C中,运行时会自动调用每个类的两个方法。+load会在类初始加载时调用,+initialize会在第一次调用类的类方法或实例方法之前被调用。这两个方法是可选的,且只有在实现了它们时才会被调用。由于method swizzling会影响到类的全局状态,因此要尽量避免在并发处理中出现竞争的情况。+load能保证在类的初始化过程中被加载,并保证这种改变应用级别的行为的一致性。相比之下,+initialize在其执行时不提供这种保证—事实上,如果在应用中没为给这个类发送消息,则它可能永远不会被调用。 Swizzling应该总是在dispatch_once中执行与上面相同,因为swizzling会改变全局状态,所以我们需要在运行时采取一些预防措施。原子性就是这样一种措施,它确保代码只被执行一次,不管有多少个线程。GCD的dispatch_once可以确保这种行为,我们应该将其作为method swizzling的最佳实践。 选择器、方法与实现在Objective-C中,选择器(selector)、方法(method)和实现(implementation)是运行时中一个特殊点,虽然在一般情况下,这些术语更多的是用在消息发送的过程描述中。 以下是Objective-C Runtime Reference中的对这几个术语一些描述: Selector(typedef struct objc_selector *SEL):用于在运行时中表示一个方法的名称。一个方法选择器是一个C字符串,它是在Objective-C运行时被注册的。选择器由编译器生成,并且在类被加载时由运行时自动做映射操作。 Method(typedef struct objc_method *Method):在类定义中表示方法的类型 Implementation(typedef id (*IMP)(id, SEL, …)):这是一个指针类型,指向方法实现函数的开始位置。这个函数使用为当前CPU架构实现的标准C调用规范。每一个参数是指向对象自身的指针(self),第二个参数是方法选择器。然后是方法的实际参数。 理解这几个术语之间的关系最好的方式是:一个类维护一个运行时可接收的消息分发表;分发表中的每个入口是一个方法(Method),其中key是一个特定名称,即选择器(SEL),其对应一个实现(IMP),即指向底层C函数的指针。 为了swizzle一个方法,我们可以在分发表中将一个方法的现有的选择器映射到不同的实现,而将该选择器对应的原始实现关联到一个新的选择器中。 调用_cmd我们回过头来看看前面新的方法的实现代码:12345- (void)xxx_viewWillAppear:(BOOL)animated { [self xxx_viewWillAppear:animated]; NSLog(@"viewWillAppear: %@", NSStringFromClass([self class]));} 咋看上去是会导致无限循环的。但令人惊奇的是,并没有出现这种情况。在swizzling的过程中,方法中的[self xxx_viewWillAppear:animated]已经被重新指定到UIViewController类的-viewWillAppear:中。在这种情况下,不会产生无限循环。不过如果我们调用的是[self viewWillAppear:animated],则会产生无限循环,因为这个方法的实现在运行时已经被重新指定为xxx_viewWillAppear:了。 注意事项Swizzling通常被称作是一种黑魔法,容易产生不可预知的行为和无法预见的后果。虽然它不是最安全的,但如果遵从以下几点预防措施的话,还是比较安全的: 总是调用方法的原始实现(除非有更好的理由不这么做):API提供了一个输入与输出约定,但其内部实现是一个黑盒。Swizzle一个方法而不调用原始实现可能会打破私有状态底层操作,从而影响到程序的其它部分。 避免冲突:给自定义的分类方法加前缀,从而使其与所依赖的代码库不会存在命名冲突。 明白是怎么回事:简单地拷贝粘贴swizzle代码而不理解它是如何工作的,不仅危险,而且会浪费学习Objective-C运行时的机会。阅读Objective-C Runtime Reference和查看头文件以了解事件是如何发生的。 小心操作:无论我们对Foundation, UIKit或其它内建框架执行Swizzle操作抱有多大信心,需要知道在下一版本中许多事可能会不一样。 扩展阅读 原文地址:Method Swizzling Objective-C Runtime Reference 南峰子的技术博客:Objective-C Runtime运行时系列教程 Objective-C的动态特性]]></content>
</entry>
<entry>
<title><![CDATA[iOS 常用数学函数]]></title>
<url>%2F2016%2F05%2F09%2Fcommon-mathematical-functions%2F</url>
<content type="text"><![CDATA[三角函数 double sin (double);正弦 double cos (double);余弦 double tan (double);正切 反三角函数 double asin (double); 结果介于[-PI/2, PI/2] double acos (double); 结果介于[0, PI] double atan (double); 反正切(主值), 结果介于[-PI/2, PI/2] double atan2 (double, double); 反正切(整圆值), 结果介于[-PI, PI] 双曲三角函数 double sinh (double); double cosh (double); double tanh (double); 指数与对数 double exp (double);求取自然数e的幂 double sqrt (double);开平方 double log (double); 以e为底的对数 double log10 (double);以10为底的对数 double pow(double x, double y);计算以x为底数的y次幂 float powf(float x, float y); 功能与pow一致,只是输入与输出皆为浮点数 取整 double ceil (double); 取上整 double floor (double); 取下整 绝对值 double fabs (double);求绝对值 double cabs(struct complex znum) ;求复数的绝对值 标准化浮点数 double frexp (double f, int p); 标准化浮点数, f = x 2^p, 已知f求x, p ( x介于[0.5, 1] ) double ldexp (double x, int p); 与frexp相反, 已知x, p求f 取整与取余 double modf (double, double*); 将参数的整数部分通过指针回传, 返回小数部分 double fmod (double, double); 返回两参数相除的余数 其他 double hypot(double x, double y);已知直角三角形两个直角边长度,求斜边长度 double ldexp(double x, int exponent);计算x(2的exponent次幂) double poly(double x, int degree, double coeffs [] );计算多项式 nt matherr(struct exception e);数学错误计算处理程序]]></content>
</entry>
<entry>
<title><![CDATA[如何创建一个私有pod库]]></title>
<url>%2F2016%2F04%2F17%2FHow-to-create-private-pod%2F</url>
<content type="text"><![CDATA[本文写的不是很详细,只是记录自己操作的一个大致过程。更加详细的操作,大家不妨参考使用Cocoapods创建私有podspec,我也是按照上面一步一步操作的,只不过个人觉得那个排版看起来真的不太友好。如果英文不错的话,其实我更建议大家看看下面的英文参考链接。 准备工作首先创建两个Git仓库,一个用来存放私有Spec Repo,另一个则存放我们私有的类库。因为Github上面私有库是要收费的,所以我选择了Coding 创建私有Spec Repo 创建本地私有Spec Repo,在终端输入下面命令即可 12# pod repo add [Private Repo Name] [Coding HTTPS clone URL]$ pod repo add DQBSpecs https://git.coding.net/qingbo/DQBSpecs.git 输完命令运行后,就在~/.cocoapods/repos/目录下自动生成了一个名为DQBSpecs的本地私有Spec Repo. 创建Pod项目工程文件如果是有现有的组件项目,并且在Git的版本管理下,那么这一步就算完成了,可以直接进行下一步了。 如果你的组件还在你冗余庞大的项目中,需要拆分出来或者需要自己从零开始创建一个组件库,那么建议你使用Cocoapods提供的一个工具将这一步与下一步结合起来做。 现在来说一下这个工具,相关的文档介绍是Using Pod Lib Create,就拿我创建的DQBLibrary为例子具体讲一下这里是如何操作的。 首先cd到要创建项目的目录下,然后执行 1$ pod lib create DQBLibrary 接下来会出现几个选项,输入你所期望的就OK啦,附上我的选项截图: 创建podspec文件如果现在有了现成的项目(是指不是通过pod lib create XXXX来创建的项目,如果是通过pod lib create命令创建的项目默认就会生成一个podspec文件)那么就需要给这个项目创建一个podspec文件,创建它需要执行Cocoapods的另外一个命令,官方文档在这里 1$ pod spec create YourLibrary git@xxxxx/YourLibrary.git 执行完之后,就创建了一个podspec文件,它其中会包含很多内容,可以按照需求进行编辑,没用的删掉。 1234567891011121314151617181920212223242526272829 Pod::Spec.new do |s| s.name = "DQBLibrary" #名称 s.version = "0.1.0" #版本号 s.summary = "Just Testing." #简短介绍,下面是详细介绍 s.description = <<-DESC Testing Private Podspec. * Markdown format. * Don't worry about the indent, we strip it! DESC s.homepage = "https://coding.net/u/qingbo/p/DQBLibrary" #主页,这里要填写可以访问到的地址,不然验证不通过 # s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2" #截图 s.license = 'MIT' #开源协议 s.author = { "DQB" => "dqb1690@163.com" } #作者信息 s.source = { :git => "https://git.coding.net/qingbo/DQBLibrary.git", :tag => s.version.to_s } #项目地址,这里不支持ssh的地址,验证不通过,只支持HTTP和HTTPS,最好使用HTTPS # s.social_media_url = 'https://twitter.com/<twitter_username>' #多媒体介绍地址 s.platform = :ios, '7.0' #支持的平台及版本 s.requires_arc = true #是否使用ARC,如果指定具体文件,则具体的问题使用ARC s.source_files = 'DQBLibrary/Classes/**/*' #代码源文件地址,**/*表示Classes目录及其子目录下所有文件,如果有多个目录下则用逗号分开,如果需要在项目中分组显示,这里也要做相应的设置 s.resource_bundles = { 'DQBLibrary' => ['DQBLibrary/Assets/*.png'] } #资源文件地址 # s.public_header_files = 'Pod/Classes/**/*.h' #公开头文件地址 # s.frameworks = 'UIKit', 'MapKit' #所需的framework,多个用逗号隔开 s.dependency 'AFNetworking', '~> 2.3' #依赖关系,该项目所依赖的其他库,如果有多个需要填写多个s.dependencyend 编辑完成之后使用验证命令验证一下 1$ pod lib lint 向Spec Repo提交podspec向Spec Repo提交podspec需要完成两点一个是podspec必须通过验证无误,在一个就是删掉无用的注释(这个不是必须的,为了规范还是删掉吧)。 向我们的私有Spec Repo提交podspec只需要一个命令 1$ pod repo push DQBSpecs DQBLibrary.podspec 完成之后这个组件库就添加到我们的私有Spec Repo中了,可以进入到~/.cocoapods/repos/DQBSpecs目录下查看 再去看我们的Spec Repo远端仓库,也有了一次提交,这个podspec也已经被Push上去了。 至此,我们的这个组件库就已经制作添加完成了,使用pod search命令就可以查到我们自己的库了 这里说的是添加到私有的Repo,如果要添加到Cocoapods的官方库了,可以使用trunk工具,具体可以查看官方文档。 使用制作好的Pod在完成这一系列步骤之后,我们就可以在正式项目中使用这个私有的Pod了,我们需要在podfile文件顶端中多定义一个source,该source地址是我们私有git地址,示例代码如下: 1234source 'https://github.com/CocoaPods/Specs.git' # 官方库source 'https://git.coding.net/qingbo/DQBLibrary.git' # 私有库pod 'DQBLibrary', :git => 'git@git.coding.net:qingbo/DQBLibrary.git', :tag => "0.1.1" 更新podspec文件首先修改工程里的podspec文件,然后pod install,将变动全部提交至对应的git服务器(工程文件对应的git地址,并非Spec文件对应的git地址),并添加一个新的tag值。然后pod lib lint先验证一下,再进行下面更新操作: 1$ pod repo push DQBSpecs PodTestLibrary.podspec #pod私有库 前面是本地Repo名字 后面是podspec名字 更新成功后,你可以在~/.cocoapods/repos/的私有库目录下查看新增了一个文件夹名为tag,该目录下生成了对应tag的podspec文件。 参考资料 使用Cocoapods创建私有podspec Private Pods Creating a Private CocoaPod Developing Private In-House Libraries with CocoaPods]]></content>
</entry>
<entry>
<title><![CDATA[ReactiveCocoa基础知识]]></title>
<url>%2F2016%2F04%2F10%2FReactiveCocoa-Basic-Use%2F</url>
<content type="text"><![CDATA[什么是ReactiveCocoa ?ReactiveCocoa(其简称为RAC)是由Github开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程和响应式编程的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。 ReactiveCocoa编程风格:函数式编程(Functional Programming):使用高阶函数,例如函数用其他函数作为参数。 响应式编程(Reactive Programming):关注于数据流和变化传播。 所以,你可能听说过ReactiveCocoa被描述为函数响应式编程(FRP)框架。编程范式是个很范的概念。 如何使用ReactiveCocoa ?教程比较好,建议边看边敲代码 ReactiveCocoa Tutorial – The Definitive Introduction: Part 1/2 ReactiveCocoa Tutorial – The Definitive Introduction: Part 2/2 对应的中文翻译版: ReactiveCocoa入门教程——第一部分 ReactiveCocoa入门教程——第二部分 扩展链接 细说ReactiveCocoa的冷信号与热信号(一) 细说ReactiveCocoa的冷信号与热信号(二):为什么要区分冷热信号 细说ReactiveCocoa的冷信号与热信号(三):怎么处理冷信号与热信号]]></content>
</entry>
<entry>
<title><![CDATA[SSH 相关知识]]></title>
<url>%2F2016%2F03%2F15%2FSSH%2F</url>
<content type="text"><![CDATA[SSH登陆的原理1. 什么是SSHSSH(安全外壳协议)为 Secure Shell 的缩写,一种网络协议,用于计算机之间的加密通信。 2. 公钥Public Key与私钥Private KeySSH Key SSH需要生成公钥Public Key和私钥Private Key, 常用的是使用RSA算法生成id_rsa.pub和id_rsa。 公钥Public Key(id_rsa.pub)是可以暴露在网络传输上的,是不安全的。而私钥Private Key(id_rsa)是不可暴露的,只能存在客户端本机上。所以公钥Public Key(id_rsa.pub)的权限是644,而私钥Private Key(id_rsa)的权限只能是600。如果权限不对,SSH会认为公钥Public Key(id_rsa.pub)和私钥Private Key(id_rsa)是不可靠的,就无法正常使用SSH登陆了。 同时在服务端会有一个~/.ssh/authorized_keys文件,里面存放了多个客户端的公钥Public Key(id_rsa.pub),就表示拥有这些Public Key的客户端就可以通过SSH登陆服务端。 3. SSH公钥登录过程客户端发出公钥登陆的请求(ssh user@host)服务端返回一段随机字符串客户端用私钥Private Key(id_rsa)加密这个字符串,再发送回服务端服务端用~/.ssh/authorized_keys里面存储的公钥Public Key去解密收到的字符串。如果成功,就表明这个客户端是可信的,客户端就可以成功登陆 由此可见,只要多台电脑上的的公钥Public Key(id_rsa.pub)和私钥Private Key(id_rsa)是一样的,对于服务端来说着其实就是同一个客户端。所以可以通过复制公钥Public Key(id_rsa.pub)和私钥Private Key(id_rsa)到多台电脑来实现共享登陆。 如何生成SSH key 在生成SSH key之前,避免可能把之前的key覆盖掉,首先可以检查以前的key是否存在,有则可以跳过第二步。(如何查看?直接在终端输入“ls ~/.ssh”查看里面是否有id_rsa和id_rsa.pub 文件) 生成SSH key很容易,只需要提供一个邮箱账号即可,切换到~/.ssh文件目录下,在终端输入: 1$ ssh-keygen -t rsa -C "your_email@example.com" 将生成的id_rsa.pub文件内容复制到相应的网站里(比如公司服务器,Github,Coding等),提供一个很方便的拷贝语句 1$ pbcopy < ~/.ssh/id_rsa.pub 测试SSH连接$ ssh -T git@github.com 如果展示下诉信息,则说明SSH连接成功 12Hi username! You've successfully authenticated, but GitHub does notprovide shell access. 扩展阅读 如何生成SSH key Connecting to GitHub with SSH]]></content>
</entry>
<entry>
<title><![CDATA[【转】一个大神开发者的使命感究竟应该是什么]]></title>
<url>%2F2016%2F03%2F08%2FThe-Role-of-a-Senior-Developer%2F</url>
<content type="text"><![CDATA[工作了五年的工程师,算不算高级开发者?归类开发者不是简单地看工作年限,因为经验这种东西千金难换但又一文不值。 我们现在工作的行业很奇怪。明明每年都有新的从业人员涌入人才市场,但企业依旧诉苦自己迫切需要大量的开发。这种问题存在已久,而且已经越发严峻。 开发者这个行业非常年轻,也面临着很严重的人才短缺问题。大部分的软件开发项目无疾而终,原因是后期开支过于庞大。那些行业里的大佬是怎么建议我们的,他们说「世界上永远存在解决问题的办法,但我们还是会遇到各种难题。因为除非我们真正去尝试解决,否则永远不知道哪种方法奏效。」 认识一个现实吧,当你从事开发工作三年以上,就可以称呼自己为「高级开发者」。但实际上,有些人只是在这个位置上混三年,而他们只是虚有其表,水平严重不符合职位。 这确实是我对同行的评语。 首先,根据知识和经验把人分为初级开发者、中级开发者,和高级开发者,这是非常一刀切地分类。有些人可能只是在一个位置上混了十年,然而他的成长甚至没有另一个人一年所学到和经验多。 开发者的高速成长期:身为一个程序员,我们生活在一个 IT 系统越发复杂且多变化的时代。有时候执行一个简单的开源项目,都需要精准定义并耗费大量时间专注任务。尤其是当你身为一个新晋程序员,或者对手下的数据库还很陌生的时候。 菜鸟程序员很难当。你可能刚从一个还不错的大学毕业,你觉得你已经掌握了基本的常识。但突然间,你在工作上面临的是另一套崭新的知识。在很长时间内,你都对自己所要做的工作没有任何头绪。而对于上司提出的要求,你似乎只能妥协,而且没办法作出有效质疑。 在这个阶段,你必须要做的是专注。而且尽可能地放开心胸,去努力学习。初级开发者在工作之初,需要大量的练习,指导、监管,以及需要一个能帮助他们的老师。缺乏以上的任何条件,初级开发者都很难很难迈过这个初学者门槛。 我最近遇到一个家伙,他已经有十年的软件构造经验,但是我很遗憾地发现,他的真实水平依旧只是初级开发者。你可以说这个阶段,是战略上的积累状态,但就是要学习加积累,积累再学习,缺一不可。 初级开发者必须要专注代码本身,在这个阶段,不要分心想任何其他乱七八糟的事情。在开发一个项目时,如果身为程序员想的是「我想让自己的代码在别人眼里看起来漂漂亮亮的」,而不是「我做的东西应该以用户感受第一」,那么他本身就是一个初级开发。 一个好的开发者可以出色地完成任务,而且不仅如此,他们能以较短时间出色完成任何,后期也会维护好。 中级开发者的瓶颈期间当一个开发者脱离菜鸟阶段成长为中级开发者时,他们能够在项目失败时反省整个过程(通常,他们会看自己所做工作部分)。而且会意识到,比起匆匆忙忙埋头苦干完成任务,应该在项目最初就建立起一些规矩。甚至于如果最开始有人指正他们,整个项目就能在最初避免走向失败。 而中级开发者还会体验另一个独特心理过程,那就是当他们回首一年前的工作。发现尽管当时认为「哎呦,自己做的还不错」,但现在会发现「这都什么玩意」啊。 一个中级开发者是有能力通过以往经验、文本资料、项目团队讨论等方式,摸索出「正确方法」做事的人。在这个阶段,学习构建软件的理论比学习构建代码更重要(后者应该在学校就掌握了)。 另一方面,中级开发者假如自作主张起来,造成的危害比初级开发者更大。初级开发者只会堆砌算法,一个好的中级开发者努力方向是「模式设计」和「范畴驱动设计」。这些技能是他们搭造 OO 系统的必备过程,学习完这些理论知识如果灵活运用,可以让他们更好地构造项目。但如果僵硬使用,也会危害整个项目。 有的时候,让一名中级开发者搭造系统,他耗费的时间可能比初级开发者更长,而且更糟的情况是他可能带整个团队走向迷途。很可悲的是,很多项目开发之所以走向失败,因为领队者自己只是一个中级开发者,他们缺乏和高级开发者工作的珍贵经验。团队领导自己,而且团队里其他人都没有意识到这点。 中级开发者很清楚自己在团队中起到的角色,能认识到他们给团队工作带来的价值。一个好的中级开发者知道代码是用来解决问题的,而不是用来终结问题的。然而,中级开发者总容易陷入一种认知上的金字塔,那就是他们会遵循一些「正确的方式」去解决问题。 一个好的中级开发者需要少而精的监管。他们在代码构造方向非常可靠,而且会在讨论设计的过程中发挥重要作用。中级开发者是团队中的「发动机」。但是,如果进一步的指导和更高级别的监管仍然是必不可少。 高级,甚至是大神开发者需要满足的条件一个高级开发者,不夸张地说,他能记住自己每次的错误。他们甚至能在设计或者编写代码的时候,就能预见到很多失败。他们会对错误进行非常敏锐地反馈,用一种诚实的方法去评估自己的成功和失败。身为高级开发者,他会更倾向于热爱复杂问题,但会痴迷于简洁地解决它。 高级开发者不会给其他开发者划分等级。与之相比,他更多的是懂得。因为懂得,所以理解每个人在每个阶段都有长处和短处。而他们也比别人更了解自己的长处和短处,力求把自己的优势最大化。 一个高级开发者会懂得,所有理论基础都有背景支持。他们不会执着于「对的方式」去搭造软件,而是把理论灵活运用于实际,理论可以变通用于为客户、团队和组织需要服务的工具。 高级开发者会在项目过程中,设身处地了解客户想要什么样的工作结果,以及他们的喜好。毕竟这些东西,比开发者个人的偏好和成功更重要。他们永远不会说「那不是我的工作」,也永远不会推搡任务和责任。 资深开发者会懂得一件事,那就是他们的工作是为了客户提供解决方案,而不是埋头写代码。而一位资深开发者永远会把自己团队能给顾客带来多少价值,放在考核标准。而不是把自己的努力和客户需求放在对等的 PK 赛场里。 诚然,因为这是一份工作,所以中间的过程总会非常枯燥和无聊。但资深开发者会退一步,思考怎么能解决和打破这些无聊的问题。他们会评估本源问题,直接解决它。或者他们会把枯燥情绪放在旁边,修复日常必须要面对的问题。 资深开发者也会懂得团队工作的高效。没有人能自己做一切工作,他们会致力于提升自己团队的效率。他们把提高团队效率所做的行为,视为提高自己软实力的一部分。 资深开发者懂得,领导力不仅仅只是权力,也不仅仅只是控制力。权力不是交通棒,而是一种服务意识。 如果你的团队中缺乏高级开发者,那这个项目基本都无一例外走向失败。拥有中级开发者能让你做事情非常快,但是在工作中你会发现,项目不仅仅只是搭造和维护程序。最终你只能关闭网站,或者用比预期中更高昂的价格维护它。只有高级开发者能选择技术和网站,而不是任由他们来伤害你。 很多需求很简单,但大多数简单需求后面,隐藏更复杂的需求。 而现实情况是,我自己很厌倦以工作时限来分类开发者。是的,工作经验能告诉你很多事情,但通常提供的都是无意义的信息。甚至于这些信息,也必须要结合很多背景来判断。 更重要的是,我们行业里需要新鲜的血液,需要招收那些刚从大学毕业充满激情的年轻人。而即使给程序员划分种类,也不可仅仅只看工作经验。实际上我们招收程序员前,应该先思考团队都需要那些人才。毕竟,我们招收的人才是为团队服务。当你招聘到错误的人选,不过是给团队和项目帮倒忙。 本文来源:mattbriggs 译文创见首发 由TECH2IPO/创见 假冒爱丽丝编译]]></content>
</entry>
<entry>
<title><![CDATA[有关NSAssert相关知识点]]></title>
<url>%2F2016%2F03%2F03%2FNSAssert%2F</url>
<content type="text"><![CDATA[苹果官方文档写的比较清楚: NSAssert Generates an assertion if a given condition is false. Declaration 1#define NSAssert(condition, desc, ...) Parameters condition:An expression that evaluates to YES or NOdesc: An NSString object that contains a printf-style string containing an error message describing the failure condition and placeholders for the arguments …:The arguments displayed in the desc string Discussion The NSAssert macro evaluates the condition and serves as a front end to the assertion handler. Each thread has its own assertion handler, which is an object of class NSAssertionHandler. When invoked, an assertion handler prints an error message that includes the method and class names (or the function name). It then raises an NSInternalInconsistencyException exception. If condition evaluates to NO, the macro invokes handleFailureInMethod:object:file:lineNumber:description: on the assertion handler for the current thread, passing desc as the description string. This macro should be used only within Objective-C methods. Assertions are disabled if the preprocessor macro NS_BLOCK_ASSERTIONS is defined. 而且在NSException.h 文件中,我们可以清楚的看到 NSAssert 的宏定义: 123456789101112#define NSAssert(condition, desc, ...) \ do { \__PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \if (!(condition)) { \ NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \ __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \ [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \ object:self file:__assert_file__ \ lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \} \ __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \ } while(0) 苹果文档提供了一种禁用Assertions的方法,在实践的过程中我又发现了另外一个禁用NSAssert的方法,详情如下: 苹果文档:“Build Settings” -> “preprocessor macro” -> 添加“NS_BLOCK_ASSERTIONS” 则禁用了Assertions 另一种方式:“Build Settings” -> “Other C Flags” -> 添加“-DNS_BLOCK_ASSERTIONS”即可]]></content>
</entry>
<entry>
<title><![CDATA[如何禁用iOS系统方法?]]></title>
<url>%2F2016%2F02%2F24%2FHow-to-disable-system-methods%2F</url>
<content type="text"><![CDATA[出于某种需求,我需要禁用iOS系统提供的某些系统方法。可以采用如下的方法: 123+(instancetype) alloc __attribute__((unavailable("call other method instead")));-(instancetype) init __attribute__((unavailable("call other method instead")));+(instancetype) new __attribute__((unavailable("call other method instead"))); 一旦我们在程序中使用到了alloc、init、new等相关方法的时候,就会出现如下错误提示:]]></content>
</entry>
<entry>
<title><![CDATA[如何给iOS应用添加原生的二维码扫描功能]]></title>
<url>%2F2016%2F01%2F23%2Fhow-add-qr-scan%2F</url>
<content type="text"><![CDATA[之前总觉得二维码扫描很高大上,其实apple工程师早就为我们提供了便捷的方法。二维码扫描第三方的库也挺多的,不过效率高的当属系统提供的扫描方法。 二维码扫描主要用到了以下几个类: AVCaptureDevice AVCaptureDeviceInput AVCaptureMetadataOutput AVCaptureSession AVCaptureVideoPreviewLayer 它们之间的关系看这篇文章 扫描的核心代码如下(最后附上完整Demo) 1234567891011121314151617181920212223242526272829303132333435363738//获取摄像设备AVCaptureDevice * device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];if (!device) return;//创建输入流AVCaptureDeviceInput * input = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil];if (!input) return;//创建输出流AVCaptureMetadataOutput * output = [[AVCaptureMetadataOutput alloc]init];if (!output) return;output.rectOfInterest = CGRectMake(0.1, 0, 0.9, 1);//设置代理 在主线程里刷新[output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];//初始化链接对象_session = [[AVCaptureSession alloc]init];//高质量采集率[_session setSessionPreset:AVCaptureSessionPresetHigh];if ([_session canAddInput:input]) { [_session addInput:input];}else{ return;}if ([_session canAddOutput:output]) { [_session addOutput:output];}else{ return;}//设置扫码支持的编码格式(如下设置条形码和二维码兼容)output.metadataObjectTypes=@[AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code];AVCaptureVideoPreviewLayer * layer = [AVCaptureVideoPreviewLayer layerWithSession:_session];layer.videoGravity = AVLayerVideoGravityResizeAspectFill;layer.frame = self.view.layer.bounds;[self.view.layer insertSublayer:layer atIndex:0];//开始捕获[_session startRunning]; Demo地址]]></content>
</entry>
<entry>
<title><![CDATA[iOS开发常用的第三方资源汇总]]></title>
<url>%2F2016%2F01%2F21%2FiOS%20developers-of-third-party-resources-summary%2F</url>
<content type="text"><![CDATA[一:第三方插件1:基于响应式编程思想的oc 地址:https://github.com/ReactiveCocoa/ReactiveCocoa 2:hud提示框 地址:https://github.com/jdg/MBProgressHUD 3:XML/HTML解析 地址:https://github.com/topfunky/hpple 4:有文字输入时,能根据键盘是否弹出来调整自身显示内容的位置 地址:https://github.com/michaeltyson/TPKeyboardAvoiding 5:状态栏提示框 地址:https://github.com/jaydee3/JDStatusBarNotification 6:block工具包。将很多需要用delegate实现的方法整合成了block的形式 地址:https://github.com/zwaldowski/BlocksKit 7:图片加载 地址:https://github.com/rs/SDWebImage 8:正则表达式 地址:https://github.com/wezm/RegexKitLite 9:Masonry代码布局 地址:https://github.com/SnapKit/Masonry 10:弹出窗 地址:https://github.com/sberrevoets/SDCAlertView 11:Button的样式 地址:https://github.com/mattlawer/BButton 12:验证网络连接状态 地址:https://github.com/tonymillion/Reachability 13:自动计算表格行高 地址:https://github.com/forkingdog/UITableView-FDTemplateLayoutCell 14:动画效果的启动页 地址:https://github.com/IFTTT/JazzHands 15:iOS快速简单集成国内三大平台分享 地址:https://github.com/xumeng/XMShareModule 16:五项能力值展示的五边形 地址:https://github.com/dsxNiubility/SXFiveScoreShow 17:自动识别网址号码邮箱和表情的label 地址:https://github.com/molon/MLEmojiLabel 18:IM对话功能的封装 地址:https://github.com/ZhipingYang/UUChatTableView 19:字典转模型框架 地址:https://github.com/CoderMJLee/MJExtension 20:下拉上拉刷数据 地址:https://github.com/CoderMJLee/MJRefresh 21:表格行左右划动菜单 地址:https://github.com/MortimerGoro/MGSwipeTableCell 22:图文混搭 地址:https://github.com/zhouande/TLAttributedLabel 23:可以简单展示在UINavigationBar下方,类似Music app的播放列表视图,弹出菜单视图 地址:https://github.com/DrummerB/BFNavigationBarDrawer 24:比如筛选、模糊、优化、蒙版、调整大小、旋转以及保存等等。同时还提供了一个UIImageView子类从URL异步加载图片,并在下载完毕时展示图片。 地址:https://github.com/Nyx0uf/NYXImagesKit 25:底部TabBar 地址:https://github.com/robbdimitrov/RDVTabBarController 26:表情面版 地址:https://github.com/ayushgoel/AGEmojiKeyboard 27:记录框架 地址:https://github.com/CocoaLumberjack/CocoaLumberjack 28:IOS与javascript交互 地址:https://github.com/marcuswestin/WebViewJavascriptBridge 29:图表统计展示 地址:https://github.com/kevinzhow/PNChart 30:appStore评分 地址:https://github.com/arashpayan/appirater 31:iOS-Categories 扩展类大全 地址:https://github.com/shaojiankui/IOS-Categories 32:扫描二维码,仿微信效果,带有扫描条 地址:https://github.com/JxbSir/JxbScanQR 33:动效弹出视图(弹出窗里面为文字,可以定义弹出的方向,及显示的时间)–AMPopTip 地址:https://github.com/andreamazz/AMPopTip 34:基于Masonry自动计算行高扩展 地址:https://github.com/632840804/HYBMasonryAutoCellHeight 35:模仿新浪微博弹出菜单 地址:https://github.com/wwdc14/HyPopMenuView 36:搜索历史标签 地址:https://github.com/zhiwupei/SearchHistory 37:快速集成新手引导的类库 地址:https://github.com/StrongX/XSportLight 38:设置页面的封装 地址:https://github.com/renzifeng/ZFSetting 39:带箭头的弹出视图插件 地址:https://github.com/xiekw2010/DXPopover 40:下拉菜单插件 地址:https://github.com/dopcn/DOPDropDownMenu/ 41:表格空白提示插件 地址:https://github.com/dzenbot/DZNEmptyDataSet 42:给任意UIView视图四条边框加上阴影,可以自定义阴影的颜色、粗细程度、透明程度以及位置(上下左右边框) 地址:https://github.com/Seitk/UIView-Shadow-Maker 43:不错的日期时间插件 地址:https://github.com/CoderXL/UUDatePicker 44:底部弹出选择 地址:https://github.com/skywinder/ActionSheetPicker-3.0 45:比较不错的引导页面插件 地址:https://github.com/ealeksandrov/EAIntroView 46:两个APP跳转的插件 地址:https://github.com/usebutton/DeepLinkKit 47:本地存取NSUserDefaults插件 地址:https://github.com/gangverk/GVUserDefaults 48:NSArray 和 NSDictionary关于LINQ的操作方式,封装一些常用的操作 地址:https://github.com/ColinEberhardt/LinqToObjectiveC 49:可以监控网络请求的内容 地址:https://github.com/coderyi/NetworkEye 50:时间帮助插件,可以快速获取时间,比较,增加等操作 地址:https://github.com/MatthewYork/DateTools 51: 不错的链式动作 地址:https://github.com/jhurray/JHChainableAnimations 52:弹出层视图,背景效果(可以自定义视图的内容) 地址:https://github.com/HJaycee/JCAlertView 53:圆形进度条的显示,中间可显示值 地址:https://github.com/mdinacci/MDRadialProgress 54:很帅的数据加载动画(可以用于数据列表加载的展现) 地址:https://github.com/NghiaTranUIT/FeSpinner 55:一个开源的AFnetworking上层的封装(猿题库等运用) 地址:https://github.com/yuantiku/YTKNetwork 56:CBStoreHouseRefreshControl:一个效果很酷炫的下拉刷新控件 地址:https://github.com/coolbeet/CBStoreHouseRefreshControl 57:AFNetworking-RACExtensions:针对ReactiveCocoa的AF封装 地址:https://github.com/CodaFi/AFNetworking-RACExtensions 58:模糊效果(毛玻璃) 地址:https://github.com/nicklockwood/FXBlurView 59:RxWebViewController:实现类似微信的 webView 导航效果,包括进度条,左滑返回上个网页或者直接关闭,就像 UINavigationController 地址:https://github.com/Roxasora/RxWebViewController 二:源代码实例1:Coding.net客户端 地址:https://coding.net/u/coding/p/Coding-iOS/git 2:高仿美团iOS版 地址:https://github.com/lookingstars/meituan 3:模仿网易新闻做的精仿网易新闻 地址:https://github.com/dsxNiubility/SXNews 4:支付宝高仿版 地址:https://github.com/gsdios/GSD_ZHIFUBAO 5:高仿百度传课iOS版 地址:https://github.com/lookingstars/chuanke 6:模仿一元云购 地址:https://github.com/JxbSir/YiYuanYunGou 7:wordpress源代码 地址:https://github.com/wordpress-mobile/WordPress-iOS 8:v2ex源代码(文章类型,若报SVProgressHUD错,则把Podfile中的SVProgressHUD移除) 地址:https://github.com/singro/v2ex 9:PHPHub客户端(IOS8.0以上) 地址:https://github.com/Aufree/phphub-ios 10:快速搭建项目源代码 地址:https://github.com/wujunyang/MobileProject 三:辅助软件1:XCODE文档注解插件VVDocumenter 地址:https://github.com/onevcat/VVDocumenter-Xcode 2:将JSON格式化输出为模型的属性 地址:https://github.com/EnjoySR/ESJsonFormat-Xcode 3:图片提示插件 地址:https://github.com/ksuther/KSImageNamed-Xcode 4:图片转换插件 地址:https://github.com/rickytan/RTImageAssets 5:转换 Xcode 控制台中一些不可阅读的字符 地址:https://github.com/dhcdht/DXXcodeConsoleUnicodePlugin 6:代码补全支持模糊查询 https://github.com/FuzzyAutocomplete/FuzzyAutocompletePlugin 7: 代码注释(command + ‘/‘)缩进【强迫症必备】 https://github.com/poboke/IndentComments 说明该文章基于CocoaChina上的一篇文章,我在此基础上补充了个人觉得还不错的插件。此文章后续会持续更新的。]]></content>
</entry>
<entry>
<title><![CDATA[Adblock Plus 浏览器插件]]></title>
<url>%2F2016%2F01%2F20%2FAdblock-Plus%2F</url>
<content type="text"><![CDATA[Adblock Plus is the most popular adblocker available for Firefox, Chrome, Opera, Safari, Android and Internet Explorer. Adblock Plus 可让您屏蔽烦人的广告、跟踪、恶意软件及其他您不想在浏览器中看到的东西。 Adblock Plus 是 Wladimir Palant 在2006年创建的一个开源项目。 最近使用chrome的时候总是弹出烦人的网页,所以才想到Adblock Plus,这里就是如何撰写过滤规则的官方教程]]></content>
</entry>
<entry>
<title><![CDATA[CocoaPods协同开发问题]]></title>
<url>%2F2016%2F01%2F04%2FCocoaPods-Collaborative-Development%2F</url>
<content type="text"><![CDATA[提出问题今天临时想在podfile 里面新增一个第三方类库,按照正常方式添加后,敲入命令pod install 后,成功引进类库。但是编译的时候始终报错:Pods-XXXX(工程名,后同)-frameworks.sh: No such file or directory,通过比对文件发现,在Pods ->Targets Support Files -> Pods-XXXX 下果然没有该文件。 分析问题由于同事已经使用了最新版本的CocoaPods来添加类库,在本地默认生成了Pods-XXXX-frameworks.sh 文件并且已经提交至Git服务器了,而我用低版本的CocoaPods来添加类库,本地并不会生成该文件。 解决问题升级CocoaPods到最新版,再Pod install一下就好啦~,由此得出的结论:同一个公司的人如果都需要进行pod install 操作的话,最好要确保每个人使用的cocoaPods的版本号一致,避免会出现一些莫名其妙的问题。 CocoaPods如何升级目前支持增量更新. 下面一行命令就OK了 1sudo gem install cocoapods 该命令就直接将最新的cocoaPods重新安装了(当然也就升级了,不过要确保gem是最新的,有没有升级成功最后把cocoaPods的版本号打印出来就知道啦),查看当前版本的cocoaPods可以用命令pod --version,如果这样升级失败了,不妨先更新gem(国内有可能需要切换源,我是没有切换的),具体方法如下: 12345678$ sudo gem update --system // 先更新gem,国内需要切换源$ gem sources --remove https://rubygems.org/$ gem sources -a https://ruby.taobao.org/$ gem sources -l\*\*\* CURRENT SOURCES \*\*\*https://ruby.taobao.org/$ sudo gem install cocoapods // 安装cocoapods$ pod setup Podfile.lock当你执行pod install之后,除了 Podfile 外,CocoaPods 还会生成一个名为Podfile.lock的文件,Podfile.lock 应该加入到版本控制里面,不应该把这个文件加入到.gitignore中。因为Podfile.lock会锁定当前各依赖库的版本,之后如果多次执行pod install 不会更改版本,要pod update才会改Podfile.lock了。这样多人协作的时候,可以防止第三方库升级时造成大家各自的第三方库版本不一致。 参考链接 CocoaPods官方文档(可能需要翻墙) CocoaPods详解之—-进阶篇 CocoaPods版本升级 CocoaPods 1.0 Migration Guide]]></content>
</entry>
<entry>
<title><![CDATA[iOS中崩溃类型汇总]]></title>
<url>%2F2015%2F09%2F28%2FiOS-crash-types%2F</url>
<content type="text"><![CDATA[在这里了解一下XCode用来表示各种崩溃类型的术语,补充一些这方面的各知识。崩溃通常是指操作系统向正在运行的程序发送的信号,所以我们在查看崩溃日志时,常常看到如下错误摘要:Application received signal SIGSEGV。一般来说,常见的崩溃类型有以下几种: EXC_BAD_ACCESS在访问一个已经释放的对象或向它发送消息时,EXC_BAD_ACCESS就会出现。造成EXC_BAD_ACCESS最常见的原因是,在初始化方法中初始化变量时用错了所有权修饰符,这会导致对象过早地被释放。举个例子,在viewDidLoad方法中为UIViewController创建了一个包含元素的NSArray,却将该数组的所有权修饰符设成了assign而不是strong。现在在viewWillAppear中,若要访问已经释放掉的对象时,就会得到名为EXC_BAD_ACCESS的崩溃。 这个崩溃发生时,查看崩溃日志,却往往得不到有用的栈信息。还好,有一个方法用来解决这个问题:NSZombieEnabled。 这是一个环境变量,用来调试与内存相关的问题,跟踪对象的释放过程。启用了NSZombieEnabled的话,它会用一个僵尸实现来去你的默认的dealloc实现,也就是在引用计数降到0时,该僵尸实现会将该对象转换成僵尸对象。僵尸对象的作用是在你向它发送消息时,它会显示一段日志并自动跳入调试器。 所以,当在应用中启用NSZombie而不是让应用直接崩溃时,一个错误的内存访问就会变成一条无法识别的消息发送给僵尸对象。僵尸对象会显示接收到的消息,然后跳入调试器,这样你就可以查看到底哪时出了问题。 可以在Xcode的scheme页面中设置NSZombieEnabled环境变量。点击Product-Edit Scheme打开该页面,然后勾选Enable Zombie Objects复选框,如图所示: 僵尸在RAC出现以前作用很大。但自从有了ARC,如果你在对象的所有权方面比较注意,那么通常不会碰到内存相关的崩溃。 SIGSEGV段错误信息(SIGSEGV)是操作系统产生的一个更严重的问题。当硬件出现错误、访问不可读的内存地址或向受保护的内存地址写入数据时,就会发生这个错误。 硬件错误这一情况并不常见。当要读取保存在RAM中的数据,而该位置的RAM硬件有问题时,你会收到SIGSEGV。SIGSEGV更多是出现在后两种情况。默认情况下,代码页不允许进行写操作,而数据而不允许进行执行操作。当应用中的某个指针指向代码页并试图修改指向位置的值时,你会收到SIGSEGV。当要读取一个指针的值,而它被初始化成指向无效内存地址的垃圾值时,你也会收到SIGSEGV。 SIGSEGV错误调试起来更困难,而导致SIGSEGV的最常见原因是不正确的类型转换。要避免过度使用指针或尝试手动修改指针来读取私有数据结构。如果你那样做了,而在修改指针时没有注意内存对齐和填充问题,就会收到SIGSEGV。 SIGBUS总线错误信号(SIGBUG)代表无效内存访问,即访问的内存是一个无效的内存地址。也就是说,那个地址指向的位置根本不是物理内存地址(它可能是某个硬件芯片的地址)。SIGSEGV和SIGBUS都羽毛球EXC_BAD_ACCESS的子类型。 SIGTRAPSIGTRAP代表陷阱信号。它并不是一个真正的崩溃信号。它会在处理器执行trap指令发送。LLDB调试器通常会处理此信号,并在指定的断点处停止运行。如果你收到了原因不明的SIGTRAP,先清除上次的输出,然后重新进行构建通常能解决这个问题。 EXC_ARITHETIC当要除零时,应用会收到EXC_ARITHMETIC信号。这个错误应该很容易解决。 SIGILLSIGILL代表signal illegal instruction(非法指令信号)。当在处理器上执行非法指令时,它就会发生。执行非法指令是指,将函数指针会给另外一个函数时,该函数指针由于某种原因是坏的,指向了一段已经释放的内存或是一个数据段。有时你收到的是EXC_BADINSTRUCTION而不是SIGILL,虽然它们是一回事,不过EXC*等同于此信号不依赖体系结构。 SIGABRTSIGABRT代表SIGNAL ABORT(中止信号)。当操作系统发现不安全的情况时,它能够对这种情况进行更多的控制;必要的话,它能要求进程进行清理工作。在调试造成此信号的底层错误时,并没有什么妙招。Cocos2d或UIKit等框架通常会在特定的前提条件没有满足或一些糟糕的情况出现时调用C函数abort(由它来发送此信号)。当SIGABRT出现时,控制台通常会输出大量的信息,说明具体哪里出错了。由于它是可控制的崩溃,所以可以在LLDB控制台上键入bt命令打印出回溯信息。 看门狗超时这种崩溃通常比较容易分辨,因为错误码是固定的0x8badf00d。(程序员也有幽默的一面,他们把它读作Ate Bad Food。)在iOS上,它经常出现在执行一个同步网络调用而阻塞主线程的情况。因此,永远不要进行同步网络调用。]]></content>
</entry>
<entry>
<title><![CDATA[NSTimer 小结]]></title>
<url>%2F2015%2F09%2F12%2FNSTimer%2F</url>
<content type="text"><![CDATA[NSTimer 常用的方法有三个 123+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep; scheduledTimerWithTimeInterval 是创建一个定时器,并加入到当前运行循环[NSRunLoop currentRunLoop]中,而其他两个只是创建定时器,并未添加到当前运行循环中,所以如果是其他两种方式创建的定时器则需要手动添加到currentRunLoop中,示例代码如下: 12345NSTimer *timer1 = [NSTimer timerWithTimeInterval:3 target:self selector:@selector(doSomeThing1) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:timer1 forMode:NSDefaultRunLoopMode]; NSTimer *timer2 = [[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:5] interval:3 target:self selector:@selector(doSomeThing2) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:timer2 forMode:NSDefaultRunLoopMode]; 关于fire方法并不是启动一个定时器,只是提前触发而已,详情可查看苹果文档 fire: You can use this method to fire a repeating timer without interrupting its regular firing schedule. If the timer is non-repeating, it is automatically invalidated after firing, even if its scheduled fire date has not arrived. 定时器如果不再使用的话,需要手动释放,直接调用invalidate即可,千万不要放在dealloc里面调用invalidate,原因我就不说了,不懂可以google invalidate: This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes its strong reference to the timer, either just before the invalidate method returns or at some later point. If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.]]></content>
</entry>
<entry>
<title><![CDATA[addObserverForName 使用注意事项]]></title>
<url>%2F2015%2F08%2F09%2FaddObserverForName%2F</url>
<content type="text"><![CDATA[避免使用addObserverForName方法给自己埋坑 挖坑准备工作之前使用通知喜欢直接在viewDidLoad里面添加下面方法 1- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString *)aName object:(nullable id)anObject; 在dealloc方法里也习惯添加 1[[NSNotificationCenter defaultCenter] removeObserver:self]; 来释放监听的对象 但是这种方法不是很直观,所以后来想用通知的block方法,由于使用不规范,埋下了很多坑,现在来填坑。先说下坑在哪里,通知的block方法如下: 1- (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block 挖坑过程在viewDidLoad直接使用上述block方法,不接收其返回值,在dealloc方法里面只添加 1[[NSNotificationCenter defaultCenter] removeObserver:self]; 挖坑结果发出一个通知,可以接收到多次 填坑攻略我还是用代码来展示吧,先定义一个 id 对象 observer 用来接收block方法的返回值 1234observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"notificationName" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { // do anything }]; 释放的时候直接removeObserver:self是没有效果的,而应该使用下列方法 123456- (void)dealloc{ if (observer) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; }} 总结addObserverForName和 addObserver的释放是不一样。]]></content>
</entry>
<entry>
<title><![CDATA[如何将数组/字典中的数据保存到plist文件中]]></title>
<url>%2F2015%2F07%2F10%2FHow-to-write-object-in-file%2F</url>
<content type="text"><![CDATA[以前总觉得将内容写入文件是多么高大上的功能,在iOS里面,其实只需要一个关键方法 1- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile; 即可实现该功能。 如何写文件该方法其实是NSArray和NSDictionary的实例方法,新手尝尝遇到的问题就是虽然用到了上述方法,但是最终写入文件失败了,失败的原因大多数就是: 要写入内容的文件是否存在(若不存在需要调用-[NSFileManager createFileAtPath:contents:attributes:]来创建文件) 要写入内容的文件已存在,但是文件路径写错了 废话不多说,附上我测试code: 1234567891011121314151617181920212223242526- (void)writeToFile{ NSDictionary *dict = @{@"A":@[@"1",@"2",@"3"], @"B":@[@"4",@"5",@"6"], @"C":@[@"7",@"8",@"9"]}; NSArray *array = @[@"A",@"B",@"C",@"D",@"E",@"F",@"G"]; //把数据保存到沙盒里的plist文件 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *plistPath = [paths objectAtIndex:0]; NSLog(@"%@",plistPath); //得到完整的路径名 NSString *fileName1 = [plistPath stringByAppendingPathComponent:@"test1.plist"]; NSString *fileName2 = [plistPath stringByAppendingPathComponent:@"test2.plist"]; NSFileManager *fm = [NSFileManager defaultManager]; if ([fm createFileAtPath:fileName1 contents:nil attributes:nil]) { [array writeToFile:fileName1 atomically:YES]; NSLog(@"文件1写入完成"); } if ([fm createFileAtPath:fileName2 contents:nil attributes:nil]) { [dict writeToFile:fileName2 atomically:YES]; NSLog(@"文件2写入完成"); }} ## 如何读文件首先需要知道你写入文件的数据的类型是什么,是字符串,一个数组,一个字典还是NSData,知道了这些,我们就可以用相应的类方法 xxxWithContentsOfFile: 来获取文件内容。 // 从文件中读取字符串,txtPath为字符串文件路径 NSString *resultStr = [NSString stringWithContentsOfFile:txtPath encoding:NSUTF8StringEncoding error:nil]; // 从文件中读取数据数组的方法,filePath为数组文件路径 NSArray *resultArr = [NSArray arrayWithContentsOfFile:filePath]; // 从文件中读取数据字典的方法,fileDicPath为字典文件路径 NSDictionary *resultDic = [NSDictionary dictionaryWithContentsOfFile:fileDicPath]; // 从文件读取存储的NSData数据,fileDataPath为数据文件路径 NSData *resultData = [NSData dataWithContentsOfFile:fileDataPath]; 扩展资料 文件操作(NSFileManager) 数据处理之文件读写]]></content>
</entry>
</search>