<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Geeklu</title>
	<atom:link href="http://geeklu.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://geeklu.com</link>
	<description></description>
	<lastBuildDate>Tue, 31 Jan 2012 05:51:40 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>Mac&amp;iOS Socket</title>
		<link>http://geeklu.com/2012/01/macios-socket/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=macios-socket</link>
		<comments>http://geeklu.com/2012/01/macios-socket/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 05:45:14 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31112</guid>
		<description><![CDATA[大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相关 5.网络编程模型 三.socket接口编程示例 四.使用select 五.使用kqueue 六.使用流 注:文档中设计涉及的代码也都在本人github目录下，分别为socketServer和socketClient.对应着各个分支。 一.Socket简介 在UNIX系统中,万物皆文件(Everything is a file)。所有的IO操作都可以看作对文件的IO操作，都遵循着这样的操作模式:打开 -> 读/写 -> 关闭，打开操作（如open函数）获取“文件”使用权，返回文件描述符，后继的操作都通过这个文件描述符来进行。很多系统调用都依赖于文件描述符,它是一个无符号整数，每一个用户进程都对应着一个文件描述符表，通过文件描述符就可以找到对应文件的信息。 在类UNIX平台上，对于控制台的标准输入输出以及标准错误输出都有对应的文件描述符，分别为0,1,2。它们定义在 1unistd.h 中 123#define &#160;STDIN_FILENO &#160; 0 &#160; /* standard input file descriptor */ #define STDOUT_FILENO &#160; 1 &#160; /* standard output file descriptor */ #define STDERR_FILENO &#160; 2 &#160; /* standard [...]]]></description>
			<content:encoded><![CDATA[<span id=""><h3>大纲</h3></span>
<ul>
<li>一.Socket简介</li>
<li>二.BSD Socket编程准备
<ul>
<li>1.地址</li>
<li>2.端口</li>
<li>3.网络字节序</li>
<li>4.半相关与全相关</li>
<li>5.网络编程模型</li>
</ul>
</li>
<li>三.socket接口编程示例</li>
<li>四.使用select</li>
<li>五.使用kqueue</li>
<li>六.使用流</li>
</ul>
<blockquote>
<p>注:文档中设计涉及的代码也都在本人github目录下，分别为socketServer和socketClient.对应着各个分支。<br />
<img id="" src="https://github.com/kejinlu/objc-doc/blob/master/img/socket_branches.png?raw=true" alt="分支" title="" /></p>
</blockquote>
<hr />
<h3 id="socket">一.Socket简介</h3>
<p>在UNIX系统中,万物皆文件(Everything is a file)。所有的IO操作都可以看作对文件的IO操作，都遵循着这样的操作模式:打开 -> 读/写 -> 关闭，打开操作（如open函数）获取“文件”使用权，返回文件描述符，后继的操作都通过这个文件描述符来进行。很多系统调用都依赖于文件描述符,它是一个无符号整数，每一个用户进程都对应着一个文件描述符表，通过文件描述符就可以找到对应文件的信息。<br />
在类UNIX平台上，对于控制台的标准输入输出以及标准错误输出都有对应的文件描述符，分别为0,1,2。它们定义在</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">unistd.h</div></td></tr></tbody></table></div>
<p>中</p>
<p><span id="more-31112"></span></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#define &nbsp;STDIN_FILENO &nbsp; 0 &nbsp; /* standard input file descriptor */<br />
#define STDOUT_FILENO &nbsp; 1 &nbsp; /* standard output file descriptor */<br />
#define STDERR_FILENO &nbsp; 2 &nbsp; /* standard error file descriptor */</div></td></tr></tbody></table></div>
</pre>
<p>在Mac系统中，可以通过Activity Monitor来查看某个进程打开的文件和端口。<br />
<img id="" src="https://github.com/kejinlu/objc-doc/blob/master/img/openedfiles.png?raw=true" alt="已打开文件" title="" /></p>
<p>UNIX内核加入TCP/IP协议的时候，便在系统中引入了一种新的IO操作，只不过由于网络连接的不可靠性，所以网络IO比本地设备的IO复杂很多。这一系列的接口叫做BSD Socket API,当初由伯克利大学研发，最终成为网络开发接口的标准。<br />
网络通信从本质上讲也是进程间通信，只是这两个进程一般在网络中不同计算机上。当然Socket API其实也提供了专门用于本地IPC的使用方式：UNIX Domain Socket，这个这里就不细说了。本文所讲的Socket如无例外，均是说的Internet Socket。</p>
<p>在本地的进程中，每一个进程都可以通过PID来标识，对于网络上的一个计算机中的进程如何标识呢？网络中的计算机可以通过一个IP地址进行标识，一个计算机中的某个进程则可以通过一个无符号整数（端口号）来标识，所以一个网络中的进程可以通过</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">IP地址+端口号</div></td></tr></tbody></table></div>
<p>的方式进行标识。 </p>
<h3 id="bsdsocket">二.BSD Socket编程准备</h3>
<span id="1."><h4>1.地址</h4></span>
<p>在程序中，我们如何保存一个地址呢？在</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&amp;lt;sys/socket.h&amp;gt;</div></td></tr></tbody></table></div>
<p>中的sockaddr便是描述socket地址的结构体类型.</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">/*<br />
* [XSI] Structure used by kernel to store most addresses.<br />
*/<br />
struct sockaddr {<br />
&nbsp; &nbsp; __uint8_t &nbsp; sa_len; &nbsp; &nbsp; /* total length */<br />
&nbsp; &nbsp; sa_family_t sa_family; &nbsp;/* [XSI] address family */<br />
&nbsp; &nbsp; char &nbsp; &nbsp; &nbsp; &nbsp;sa_data[14]; &nbsp; &nbsp;/* [XSI] addr value (actually larger) */<br />
};</div></td></tr></tbody></table></div>
</pre>
<p>为了方便设置用语网络通信的socket地址，引入了sockaddr_in结构体（对于UNIX Domain Socket则对应sockaddr_un）</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">/*<br />
&nbsp;* Socket address, internet style.<br />
&nbsp;*/<br />
struct sockaddr_in {<br />
&nbsp; &nbsp; __uint8_t &nbsp; sin_len;<br />
&nbsp; &nbsp; sa_family_t sin_family;<br />
&nbsp; &nbsp; in_port_t &nbsp; sin_port;//得是网络字节序<br />
&nbsp; &nbsp; struct &nbsp;in_addr sin_addr;//in_addr存在的原因则是历史原因，其实质是代表一个IP地址的32位整数<br />
&nbsp; &nbsp; char &nbsp; &nbsp; &nbsp; &nbsp;sin_zero[8];//bzero之，纯粹是为了兼容sockaddr<br />
};</div></td></tr></tbody></table></div>
</pre>
<p>在实际编程的时候，经常需要将sockaddr_in强制转换成sockaddr类型。    </p>
<span id="2."><h4>2.端口</h4></span>
<p>说到端口我们经常会联想到硬件，在网络编程中的端口其实是一个标识而已，或者说是系统的资源而已。系统提供了端口分配和管理的机制。</p>
<span id="3."><h4>3.网络字节序</h4></span>
<p>谈网络字节序(Endianness)之前我们先说说什么是字节序。字节序又叫端序，就是指计算机中存放 <strong>多字节数据</strong>的字节的顺序。典型的就是数据存放在内存中或者网络传输时的字节的顺序。常用的字节序有大端序(big-endian)，小端序(litle-endian,另还有不常见的混合序middle-endian)。不同的CPU可能会使用不同的字节序，如X86，PDP-11等处理器为小端序，Motorola 6800,PowerPC 970等使用的是大端序。小端序是指低字节位存放在内存地址的低端，高端序是指高位字节存放在内存的低端。<br />
举个例子来说明什么是大端序和小端序：<br />
比如一个4字节的整数 16进制形式为 0&#215;12345678，最左边是高位。</p>
<p>大端序</p>
<table>
<col align="left" />
<col align="left" />
<col align="left" />
<col align="left" />
<thead>
<tr>
<th>低位</th>
<th>> ></th>
<th>> ></th>
<th>高位</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">12</td>
<td align="left">34</td>
<td align="left">56</td>
<td align="left">78</td>
</tr>
</tbody>
</table>
<p>小端序</p>
<table>
<col align="left" />
<col align="left" />
<col align="left" />
<col align="left" />
<thead>
<tr>
<th>低位</th>
<th>> ></th>
<th>> ></th>
<th>高位</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">78</td>
<td align="left">56</td>
<td align="left">34</td>
<td align="left">12</td>
</tr>
</tbody>
</table>
<p>TCP/IP 各层协议将字节序使用的是大端序，我们把TCP/IP协议中使用的字节序称之为网络字节序。<br />
 编程的时候可以使用定义在</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">sys/_endian.h</div></td></tr></tbody></table></div>
<p>中的相关的接口进行本地字节序和网络字节序的互转。</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#define ntohs(x) &nbsp; &nbsp;__DARWIN_OSSwapInt16(x) // 16位整数 网络字节序转主机字节序<br />
#define htons(x) &nbsp; &nbsp;__DARWIN_OSSwapInt16(x) // 16位整数 主机字节序转网络字节序<br />
<br />
#define ntohl(x) &nbsp; &nbsp;__DARWIN_OSSwapInt32(x) &nbsp;//32位整数 网络字节序转主机字节序<br />
#define htonl(x) &nbsp; &nbsp;__DARWIN_OSSwapInt32(x) //32位整数 主机字节序转网络字节序</div></td></tr></tbody></table></div>
</pre>
<blockquote>
<p>以上声明中 n代表netwrok， h代表host ，s代表short，l代表long</p>
</blockquote>
<p>如果数据是单字节的话，则其没有字节序的说法了。</p>
<span id="4."><h4>4.半相关与全相关</h4></span>
<p>半相关（half-association）是指一个三元组</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">(协议,本地IP地址,本地端口)</div></td></tr></tbody></table></div>
<p>,通过这个三元组就可以唯一标识一个网络中的进程,一般用于listening socket。但是实际进行通信的过程，至少需要两个进程，且它们所使用的协议必须一致，所以一个完成的网络通信至少需要一个五元组表示</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">(协议,本地地址,本地端口,远端地址,远端端口)</div></td></tr></tbody></table></div>
<p>，这样的五元组叫做全相关。</p>
<span id="5."><h4>5.网络编程模型</h4></span>
<p>网络存在的本质其实就是网络中个体之间的在某个领域的信息存在不对等性，所以一般情况下总有一些个体为另一些个体提供服务。提供服务器的我们把它叫做服务器，接受服务的叫做客户端。所以在网络编程中，也存在服务器端和客户端之分。</p>
<table>
<col align="left" />
<col align="left" />
<thead>
<tr>
<th>服务器端</th>
<th>客户端</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">创建Socket</td>
<td align="left">-</td>
</tr>
<tr>
<td align="left">将Socket和本地的地址端口绑定</td>
<td align="left">-</td>
</tr>
<tr>
<td align="left">开始进行侦听</td>
<td align="left">创建一个Socket和服务器的地址并通过它们向服务器发送连接请求</td>
</tr>
<tr>
<td align="left">握手成功，接受请求，得到一个新的Socket，通过它可以和客户端进行通信</td>
<td align="left">连接成功，客户端的Socket会绑定到系统分配的一个端口上，并可以通过它和服务器端进行通信</td>
</tr>
</tbody>
</table>
<h3 id="bsdsocket">三.BSD Socket编程详解</h3>
<p>下面的例子是一个简单的一对一聊天的程序，分服务器和客户端，且发送消息和接受消息次序固定。</p>
<h4 id="server">Server端代码</h4>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#include &amp;lt;stdio.h&amp;gt;<br />
#include &amp;lt;netinet/in.h&amp;gt;<br />
#include &amp;lt;sys/socket.h&amp;gt;<br />
#include &amp;lt;arpa/inet.h&amp;gt;<br />
#include &amp;lt;string.h&amp;gt;<br />
<br />
int main (int argc, const char * argv[])<br />
{<br />
&nbsp; &nbsp; struct sockaddr_in server_addr;<br />
&nbsp; &nbsp; server_addr.sin_len = sizeof(struct sockaddr_in);<br />
&nbsp; &nbsp; server_addr.sin_family = AF_INET;//Address families AF_INET互联网地址簇<br />
&nbsp; &nbsp; server_addr.sin_port = htons(11332);<br />
&nbsp; &nbsp; server_addr.sin_addr.s_addr = inet_addr(&quot;127.0.0.1&quot;);<br />
&nbsp; &nbsp; bzero(&amp;amp;(server_addr.sin_zero),8);<br />
<br />
&nbsp; &nbsp; //创建socket<br />
&nbsp; &nbsp; int server_socket = socket(AF_INET, SOCK_STREAM, 0);//SOCK_STREAM 有连接<br />
&nbsp; &nbsp; if (server_socket == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;socket error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; //绑定socket：将创建的socket绑定到本地的IP地址和端口，此socket是半相关的，只是负责侦听客户端的连接请求，并不能用于和客户端通信<br />
&nbsp; &nbsp; int bind_result = bind(server_socket, (struct sockaddr *)&amp;amp;server_addr, sizeof(server_addr));<br />
&nbsp; &nbsp; if (bind_result == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;bind error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; //listen侦听 第一个参数是套接字，第二个参数为等待接受的连接的队列的大小，在connect请求过来的时候,完成三次握手后先将连接放到这个队列中，直到被accept处理。如果这个队列满了，且有新的连接的时候，对方可能会收到出错信息。<br />
&nbsp; &nbsp; if (listen(server_socket, 5) == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;listen error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; struct sockaddr_in client_address;<br />
&nbsp; &nbsp; socklen_t address_len;<br />
&nbsp; &nbsp; int client_socket = accept(server_socket, (struct sockaddr *)&amp;amp;client_address, &amp;amp;address_len);<br />
&nbsp; &nbsp; //返回的client_socket为一个全相关的socket，其中包含client的地址和端口信息，通过client_socket可以和客户端进行通信。<br />
&nbsp; &nbsp; if (client_socket == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;accept error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return -1;<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; char recv_msg[1024];<br />
&nbsp; &nbsp; char reply_msg[1024];<br />
<br />
&nbsp; &nbsp; while (1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; bzero(recv_msg, 1024);<br />
&nbsp; &nbsp; &nbsp; &nbsp; bzero(reply_msg, 1024);<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;reply:&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; scanf(&quot;%s&quot;,reply_msg);<br />
&nbsp; &nbsp; &nbsp; &nbsp; send(client_socket, reply_msg, 1024, 0);<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; long byte_num = recv(client_socket,recv_msg,1024,0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; recv_msg[byte_num] = '\0';<br />
&nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;client said:%s\n&quot;,recv_msg);<br />
<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; return 0;<br />
}</div></td></tr></tbody></table></div>
</pre>
<h4 id="client">Client端代码</h4>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#include &amp;lt;stdio.h&amp;gt;<br />
#include &amp;lt;netinet/in.h&amp;gt;<br />
#include &amp;lt;sys/socket.h&amp;gt;<br />
#include &amp;lt;arpa/inet.h&amp;gt;<br />
#include &amp;lt;string.h&amp;gt;<br />
<br />
int main (int argc, const char * argv[])<br />
{<br />
&nbsp; &nbsp; struct sockaddr_in server_addr;<br />
&nbsp; &nbsp; server_addr.sin_len = sizeof(struct sockaddr_in);<br />
&nbsp; &nbsp; server_addr.sin_family = AF_INET;<br />
&nbsp; &nbsp; server_addr.sin_port = htons(11332);<br />
&nbsp; &nbsp; server_addr.sin_addr.s_addr = inet_addr(&quot;127.0.0.1&quot;);<br />
&nbsp; &nbsp; bzero(&amp;amp;(server_addr.sin_zero),8);<br />
<br />
&nbsp; &nbsp; int server_socket = socket(AF_INET, SOCK_STREAM, 0);<br />
&nbsp; &nbsp; if (server_socket == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;socket error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; char recv_msg[1024];<br />
&nbsp; &nbsp; char reply_msg[1024];<br />
<br />
&nbsp; &nbsp; if (connect(server_socket, (struct sockaddr *)&amp;amp;server_addr, sizeof(struct sockaddr_in))==0) &nbsp; &nbsp; {<br />
&nbsp; &nbsp; //connect 成功之后，其实系统将你创建的socket绑定到一个系统分配的端口上，且其为全相关，包含服务器端的信息，可以用来和服务器端进行通信。<br />
&nbsp; &nbsp; &nbsp; &nbsp; while (1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(recv_msg, 1024);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(reply_msg, 1024);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; long byte_num = recv(server_socket,recv_msg,1024,0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recv_msg[byte_num] = '\0';<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;server said:%s\n&quot;,recv_msg);<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;reply:&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; scanf(&quot;%s&quot;,reply_msg);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (send(server_socket, reply_msg, 1024, 0) == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;send error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; // insert code here...<br />
&nbsp; &nbsp; printf(&quot;Hello, World!\n&quot;);<br />
&nbsp; &nbsp; return 0;<br />
}</div></td></tr></tbody></table></div>
</pre>
<p>上面的服务器端和客户端连接成功之后打开的端口的情况是怎么样的呢？</p>
<ul>
<li>
<p>服务器端 ,存在一个用于listen的半相关的socket，一个用于和客户端进行通信的全相关的socket<br />
<img id="" src="https://github.com/kejinlu/objc-doc/blob/master/img/socket_server_opened_files.png?raw=true" alt="服务器端进程打开文件" title="" /></p>
</li>
<li>
<p>客户端 存在一个用于和服务器端进行通信的全相关的socket<br />
<img id="" src="https://github.com/kejinlu/objc-doc/blob/master/img/socket_client_opened_files.png?raw=true" alt="客户端进程打开文件" title="" /></p>
</li>
</ul>
<p>由于accept只运行了一次，所以服务器端一次只能和一个客户端进行通信，且使用的send和recv方法都是阻塞的，所以上面这个例子存在一个问题就是服务器端客户端连接成功之后，发送，接受，发送，接受的次序就被固定了。比如服务器端发送消息之后就等客户端发送消息了，没有接受到客户端的消息之前服务器端是没有办法发送消息的。使用select这个这个系统调用可以解决上面的问题。</p>
<h3 id="select">四.使用select</h3>
<p>select这个系统调用，是一种多路复用IO方案，可以同时对多个文件描述符进行监控，从而知道哪些文件描述符可读，可写或者出错，不过select方法是阻塞的，可以设定超时时间。<br />
 select使用的步骤如下:</p>
<ul>
<li>1.创建一个fd_set变量（fd_set实为包含了一个整数数组的结构体），用来存放所有的待检查的文件描述符</li>
<li>2.清空fd_set变量，并将需要检查的所有文件描述符加入fd_set</li>
<li>3.调用select。若返回-1，则说明出错;返回0,则说明超时，返回正数，则为发生状态变化的文件描述符的个数</li>
<li>4.若select返回大于0,则依次查看哪些文件描述符变的可读，并对它们进行处理</li>
<li>5.返回步骤2，开始新一轮的检测</li>
</ul>
<p>若上面的聊天程序使用select进行改写，则是下面这样的</p>
<span id="_1"><h4>服务器端</h4></span>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br />139<br />140<br />141<br />142<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; #include &amp;lt;stdio.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;stdlib.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;netinet/in.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;sys/socket.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;arpa/inet.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;string.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;unistd.h&amp;gt;<br />
&nbsp; &nbsp; #define BACKLOG 5 //完成三次握手但没有accept的队列的长度<br />
&nbsp; &nbsp; #define CONCURRENT_MAX 8 //应用层同时可以处理的连接<br />
&nbsp; &nbsp; #define SERVER_PORT 11332<br />
&nbsp; &nbsp; #define BUFFER_SIZE 1024<br />
&nbsp; &nbsp; #define QUIT_CMD &quot;.quit&quot;<br />
&nbsp; &nbsp; int client_fds[CONCURRENT_MAX];<br />
&nbsp; &nbsp; int main (int argc, const char * argv[])<br />
&nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; char input_msg[BUFFER_SIZE];<br />
&nbsp; &nbsp; &nbsp; &nbsp; char recv_msg[BUFFER_SIZE]; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; //本地地址<br />
&nbsp; &nbsp; &nbsp; &nbsp; struct sockaddr_in server_addr;<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_len = sizeof(struct sockaddr_in);<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_family = AF_INET;<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_port = htons(SERVER_PORT);<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_addr.s_addr = inet_addr(&quot;127.0.0.1&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; bzero(&amp;amp;(server_addr.sin_zero),8);<br />
&nbsp; &nbsp; &nbsp; &nbsp; //创建socket<br />
&nbsp; &nbsp; &nbsp; &nbsp; int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (server_sock_fd == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;socket error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; //绑定socket<br />
&nbsp; &nbsp; &nbsp; &nbsp; int bind_result = bind(server_sock_fd, (struct sockaddr *)&amp;amp;server_addr, sizeof(server_addr));<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (bind_result == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;bind error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; //listen<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (listen(server_sock_fd, BACKLOG) == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;listen error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; //fd_set<br />
&nbsp; &nbsp; &nbsp; &nbsp; fd_set server_fd_set;<br />
&nbsp; &nbsp; &nbsp; &nbsp; int max_fd = -1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; struct timeval tv;<br />
&nbsp; &nbsp; &nbsp; &nbsp; tv.tv_sec = 20;<br />
&nbsp; &nbsp; &nbsp; &nbsp; tv.tv_usec = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; while (1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_ZERO(&amp;amp;server_fd_set);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //标准输入<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_SET(STDIN_FILENO, &amp;amp;server_fd_set);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (max_fd &amp;lt; STDIN_FILENO) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max_fd = STDIN_FILENO;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //服务器端socket<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_SET(server_sock_fd, &amp;amp;server_fd_set);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (max_fd &amp;lt; server_sock_fd) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max_fd = server_sock_fd;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //客户端连接<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &amp;lt; CONCURRENT_MAX; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_fds[i]!=0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_SET(client_fds[i], &amp;amp;server_fd_set);<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (max_fd &amp;lt; client_fds[i]) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; max_fd = client_fds[i];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int ret = select(max_fd+1, &amp;amp;server_fd_set, NULL, NULL, &amp;amp;tv);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (ret &amp;lt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;select 出错\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else if(ret == 0){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;select 超时\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //ret为未状态发生变化的文件描述符的个数<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (FD_ISSET(STDIN_FILENO, &amp;amp;server_fd_set)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //标准输入<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(input_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fgets(input_msg, BUFFER_SIZE, stdin);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //输入 &quot;.quit&quot; 则退出服务器<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (strcmp(input_msg, QUIT_CMD) == 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exit(0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i=0; i&amp;lt;CONCURRENT_MAX; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_fds[i]!=0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; send(client_fds[i], input_msg, BUFFER_SIZE, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (FD_ISSET(server_sock_fd, &amp;amp;server_fd_set)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //有新的连接请求<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; struct sockaddr_in client_address;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; socklen_t address_len;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int client_socket_fd = accept(server_sock_fd, (struct sockaddr *)&amp;amp;client_address, &amp;amp;address_len);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_socket_fd &amp;gt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int index = -1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &amp;lt; CONCURRENT_MAX; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_fds[i] == 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index = i;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; client_fds[i] = client_socket_fd;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (index &amp;gt;= 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;新客户端(%d)加入成功 %s:%d \n&quot;,index,inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(input_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strcpy(input_msg, &quot;服务器加入的客户端数达到最大值,无法加入!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; send(client_socket_fd, input_msg, BUFFER_SIZE, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;客户端连接数达到最大值，新客户端加入失败 %s:%d \n&quot;,inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &amp;lt;CONCURRENT_MAX; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_fds[i]!=0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (FD_ISSET(client_fds[i], &amp;amp;server_fd_set)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //处理某个客户端过来的消息<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(recv_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; long byte_num = recv(client_fds[i],recv_msg,BUFFER_SIZE,0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (byte_num &amp;gt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (byte_num &amp;gt; BUFFER_SIZE) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; byte_num = BUFFER_SIZE;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recv_msg[byte_num] = '\0';<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;客户端(%d):%s\n&quot;,i,recv_msg);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else if(byte_num &amp;lt; 0){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;从客户端(%d)接受消息出错.\n&quot;,i);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_CLR(client_fds[i], &amp;amp;server_fd_set);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; client_fds[i] = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;客户端(%d)退出了\n&quot;,i);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 0;<br />
&nbsp; &nbsp; }</div></td></tr></tbody></table></div>
</pre>
<span id="_2"><h4>客户端</h4></span>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; #include &amp;lt;stdio.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;netinet/in.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;sys/socket.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;arpa/inet.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;string.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;unistd.h&amp;gt;<br />
&nbsp; &nbsp; #include &amp;lt;stdlib.h&amp;gt;<br />
<br />
&nbsp; &nbsp; #define BUFFER_SIZE 1024<br />
<br />
&nbsp; &nbsp; int main (int argc, const char * argv[])<br />
&nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; struct sockaddr_in server_addr;<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_len = sizeof(struct sockaddr_in);<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_family = AF_INET;<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_port = htons(11332);<br />
&nbsp; &nbsp; &nbsp; &nbsp; server_addr.sin_addr.s_addr = inet_addr(&quot;127.0.0.1&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; bzero(&amp;amp;(server_addr.sin_zero),8);<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (server_sock_fd == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;socket error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; char recv_msg[BUFFER_SIZE];<br />
&nbsp; &nbsp; &nbsp; &nbsp; char input_msg[BUFFER_SIZE];<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (connect(server_sock_fd, (struct sockaddr *)&amp;amp;server_addr, sizeof(struct sockaddr_in))==0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fd_set client_fd_set;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; struct timeval tv;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tv.tv_sec = 20;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; tv.tv_usec = 0;<br />
<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while (1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_ZERO(&amp;amp;client_fd_set);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_SET(STDIN_FILENO, &amp;amp;client_fd_set);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FD_SET(server_sock_fd, &amp;amp;client_fd_set);<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int ret = select(server_sock_fd + 1, &amp;amp;client_fd_set, NULL, NULL, &amp;amp;tv);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (ret &amp;lt; 0 ) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;select 出错!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else if(ret ==0){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;select 超时!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (FD_ISSET(STDIN_FILENO, &amp;amp;client_fd_set)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(input_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fgets(input_msg, BUFFER_SIZE, stdin);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (send(server_sock_fd, input_msg, BUFFER_SIZE, 0) == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;发送消息出错!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (FD_ISSET(server_sock_fd, &amp;amp;client_fd_set)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(recv_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; long byte_num = recv(server_sock_fd,recv_msg,BUFFER_SIZE,0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (byte_num &amp;gt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (byte_num &amp;gt; BUFFER_SIZE) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; byte_num = BUFFER_SIZE;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recv_msg[byte_num] = '\0';<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;服务器:%s\n&quot;,recv_msg);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else if(byte_num &amp;lt; 0){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;接受消息出错!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;服务器端退出!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exit(0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 0;<br />
&nbsp; &nbsp; }</div></td></tr></tbody></table></div>
</pre>
<p>当然select也有其局限性。当fd_set中的文件描述符较少，或者大都数文件描述符都比较活跃的时候，select的效率还是不错的。Mac系统中已经定义了fd_set 最大可以容纳的文件描述符的个数为1024</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">//sys/_structs.h<br />
#define __DARWIN_FD_SETSIZE 1024<br />
/////////////////////////////////////////////<br />
//Kernel.framework sys/select.h<br />
#define FD_SETSIZE &nbsp;__DARWIN_FD_SETSIZE</div></td></tr></tbody></table></div>
</pre>
<p>每一次select 调用的时候，都涉及到user space和kernel space的内存拷贝，且会对fd_set中的所有文件描述符进行遍历，如果所有的文件描述符均不满足，且没有超时，则当前进程便开始睡眠，直到超时或者有文件描述符状态发生变化。当文件描述符数量较大的时候，将耗费大量的CPU时间。所以后来有新的方案出现了，如windows2000引入的IOCP，Linux Kernel 2.6中成熟的epoll，FreeBSD4.x引入的kqueue。</p>
<h3 id="kqueue">五.使用kqueue</h3>
<p>Mac是基于BSD的内核，所使用的是kqueue（kernel event notification mechanism，详细内容可以Mac中</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">man 2 kqueue</div></td></tr></tbody></table></div>
<p>），kqueue比select先进的地方就在于使用事件触发的机制，且其调用无需每次对所有的文件描述符进行遍历，返回的时候只返回需要处理的事件，而不像select中需要自己去一个个通过FD_ISSET检查。 <br />
kqueue默认的触发方式是level 水平触发，可以通过设置event的flag为</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">EV_CLEAR</div></td></tr></tbody></table></div>
<p>使得这个事件变为边沿触发,可能epoll的触发方式无法细化到单个event，需要查证。</p>
<p>kqueue中涉及两个系统调用，kqueue()和kevent()</p>
<ul>
<li>kqueue() 创建kernel级别的事件队列，并返回队列的文件描述符</li>
<li>kevent() 往事件队列中加入订阅事件，或者返回相关的事件数组</li>
</ul>
<p>kqueue使用的流程一般如下：</p>
<ul>
<li>创建kqueue</li>
<li>创建struct kevent变量（注意这里的kevent是结构体类型名），可以通过EV_SET这个宏提供的快捷方式进行创建</li>
<li>通过kevent系统调用将创建好的kevent结构体变量加入到kqueue队列中，完成对指定文件描述符的事件的订阅</li>
<li>
<p>通过kevent系统调用获取满足条件的事件队列，并对每一个事件进行处理</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br />84<br />85<br />86<br />87<br />88<br />89<br />90<br />91<br />92<br />93<br />94<br />95<br />96<br />97<br />98<br />99<br />100<br />101<br />102<br />103<br />104<br />105<br />106<br />107<br />108<br />109<br />110<br />111<br />112<br />113<br />114<br />115<br />116<br />117<br />118<br />119<br />120<br />121<br />122<br />123<br />124<br />125<br />126<br />127<br />128<br />129<br />130<br />131<br />132<br />133<br />134<br />135<br />136<br />137<br />138<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#include &amp;lt;stdio.h&amp;gt;<br />
#include &amp;lt;stdlib.h&amp;gt;<br />
#include &amp;lt;netinet/in.h&amp;gt;<br />
#include &amp;lt;sys/socket.h&amp;gt;<br />
#include &amp;lt;sys/event.h&amp;gt;<br />
#include &amp;lt;sys/types.h&amp;gt;<br />
#include &amp;lt;sys/time.h&amp;gt;<br />
#include &amp;lt;arpa/inet.h&amp;gt;<br />
#include &amp;lt;string.h&amp;gt;<br />
#include &amp;lt;unistd.h&amp;gt;<br />
#define BACKLOG 5 //完成三次握手但没有accept的队列的长度<br />
#define CONCURRENT_MAX 8 //应用层同时可以处理的连接<br />
#define SERVER_PORT 11332<br />
#define BUFFER_SIZE 1024<br />
#define QUIT_CMD &quot;.quit&quot;<br />
int client_fds[CONCURRENT_MAX];<br />
struct kevent events[10];//CONCURRENT_MAX + 2<br />
int main (int argc, const char * argv[])<br />
{<br />
&nbsp; &nbsp; char input_msg[BUFFER_SIZE];<br />
&nbsp; &nbsp; char recv_msg[BUFFER_SIZE];<br />
&nbsp; &nbsp; //本地地址<br />
&nbsp; &nbsp; struct sockaddr_in server_addr;<br />
&nbsp; &nbsp; server_addr.sin_len = sizeof(struct sockaddr_in);<br />
&nbsp; &nbsp; server_addr.sin_family = AF_INET;<br />
&nbsp; &nbsp; server_addr.sin_port = htons(SERVER_PORT);<br />
&nbsp; &nbsp; server_addr.sin_addr.s_addr = inet_addr(&quot;127.0.0.1&quot;);<br />
&nbsp; &nbsp; bzero(&amp;amp;(server_addr.sin_zero),8);<br />
&nbsp; &nbsp; //创建socket<br />
&nbsp; &nbsp; int server_sock_fd = socket(AF_INET, SOCK_STREAM, 0);<br />
&nbsp; &nbsp; if (server_sock_fd == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;socket error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; //绑定socket<br />
&nbsp; &nbsp; int bind_result = bind(server_sock_fd, (struct sockaddr *)&amp;amp;server_addr, sizeof(server_addr));<br />
&nbsp; &nbsp; if (bind_result == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;bind error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; //listen<br />
&nbsp; &nbsp; if (listen(server_sock_fd, BACKLOG) == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;listen error&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; struct timespec timeout = {10,0};<br />
&nbsp; &nbsp; //kqueue<br />
&nbsp; &nbsp; int kq = kqueue();<br />
&nbsp; &nbsp; if (kq == -1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; perror(&quot;创建kqueue出错!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; exit(1);<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; struct kevent event_change;<br />
&nbsp; &nbsp; EV_SET(&amp;amp;event_change, STDIN_FILENO, EVFILT_READ, EV_ADD, 0, 0, NULL);<br />
&nbsp; &nbsp; kevent(kq, &amp;amp;event_change, 1, NULL, 0, NULL);<br />
&nbsp; &nbsp; EV_SET(&amp;amp;event_change, server_sock_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);<br />
&nbsp; &nbsp; kevent(kq, &amp;amp;event_change, 1, NULL, 0, NULL);<br />
&nbsp; &nbsp; while (1) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; int ret = kevent(kq, NULL, 0, events, 10, &amp;amp;timeout);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (ret &amp;lt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;kevent 出错!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }else if(ret == 0){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;kenvent 超时!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //ret &amp;gt; 0 返回事件放在events中<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &amp;lt; ret; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; struct kevent current_event = events[i];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //kevent中的ident就是文件描述符<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (current_event.ident == STDIN_FILENO) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //标准输入<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(input_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fgets(input_msg, BUFFER_SIZE, stdin);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //输入 &quot;.quit&quot; 则退出服务器<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (strcmp(input_msg, QUIT_CMD) == 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; exit(0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i=0; i&amp;lt;CONCURRENT_MAX; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_fds[i]!=0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; send(client_fds[i], input_msg, BUFFER_SIZE, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else if(current_event.ident == server_sock_fd){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //有新的连接请求<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; struct sockaddr_in client_address;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; socklen_t address_len;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int client_socket_fd = accept(server_sock_fd, (struct sockaddr *)&amp;amp;client_address, &amp;amp;address_len);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_socket_fd &amp;gt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; int index = -1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &amp;lt; CONCURRENT_MAX; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_fds[i] == 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; index = i;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; client_fds[i] = client_socket_fd;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (index &amp;gt;= 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EV_SET(&amp;amp;event_change, client_socket_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; kevent(kq, &amp;amp;event_change, 1, NULL, 0, NULL);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;新客户端(fd = %d)加入成功 %s:%d \n&quot;,client_socket_fd,inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(input_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; strcpy(input_msg, &quot;服务器加入的客户端数达到最大值,无法加入!\n&quot;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; send(client_socket_fd, input_msg, BUFFER_SIZE, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;客户端连接数达到最大值，新客户端加入失败 %s:%d \n&quot;,inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //处理某个客户端过来的消息<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bzero(recv_msg, BUFFER_SIZE);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; long byte_num = recv((int)current_event.ident,recv_msg,BUFFER_SIZE,0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (byte_num &amp;gt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (byte_num &amp;gt; BUFFER_SIZE) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; byte_num = BUFFER_SIZE;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; recv_msg[byte_num] = '\0';<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;客户端(fd = %d):%s\n&quot;,(int)current_event.ident,recv_msg);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else if(byte_num &amp;lt; 0){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;从客户端(fd = %d)接受消息出错.\n&quot;,(int)current_event.ident);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EV_SET(&amp;amp;event_change, current_event.ident, EVFILT_READ, EV_DELETE, 0, 0, NULL);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; kevent(kq, &amp;amp;event_change, 1, NULL, 0, NULL);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; close((int)current_event.ident);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &amp;lt; CONCURRENT_MAX; i++) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (client_fds[i] == (int)current_event.ident) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; client_fds[i] = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;客户端(fd = %d)退出了\n&quot;,(int)current_event.ident);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; return 0;<br />
}</div></td></tr></tbody></table></div>
</pre>
</li>
</ul>
<p>其实kqueue的应用场景非常的广阔，可以监控文件系统中文件的变化（对文件变化的事件可以粒度非常的细，具体可以查看kqueue的手册），监控系统进程的生命周期。GCD的事件处理便是建立在kqueue之上的。</p>
<h3 id="streams">六.使用Streams</h3>
<p>使用Objective-C的一大优点便是面向对象编程，使得逻辑抽象得更加优美，更加符合人类思维。<br />
一开始说过，无论是对于文件的操作或者对于网络的操作，本质上都是IO操作，无非写数据和读数据，可以对这种输入输出进行抽象，抽象成输入流和输出流， <strong>从输入流中读取数据，往输出流中写数据</strong>。<br />
Cocoa中的NSInputStream和NSOutputStream便是输入流和输出流的抽象，它们的实现分别基于CoreFoundation中的CFReadStream和CFWriteStream。<br />
输入输出流对runloop有很好的支持。<br />
NSInputStream和CFReadStream以及NSOutputStream和CFWriteStream之间可以通过 &#8220;toll-free bridging&#8221;实现无缝的类型转换。<br />
CoreFoundation中的CFStream提供了输入输出流和CFSocket绑定的函数。<br />
这样便可以通过输入输出流和远端进行通信了。</p>
<p>首先通过XCode创建一个Foundation(C的也行，但是你得将</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">main.c</div></td></tr></tbody></table></div>
<p>改成</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">main.m</div></td></tr></tbody></table></div>
<p>)的命令行项目.<br />
创建一个ChatServer的类，包含一个run的方法。在Cocoa的程序中有一点是和C语言不同的，你无需自己去写一个死循环充当runloop，框架本身就对runloop进行了支持，需要做的就是将事件源加入到当前线程的runloop中，然后启动runloop。<br />
所以在run方法中，创建好用于侦听连接请求的socket，socket有对应的处理连接accept的回调函数，以及把它封装成runloop的输入源，加入到当前runloop。<br />
我们还得从标准输入获取需要发送消息，所以使用了CFFileDescriptor，它是文件描述符的objc的封装，加入了runloop的支持，通过它可以将标准输入以输入源的方法加入到当前runloop，当标准输入缓冲区有数据可读的时候，设置好的回调函数便会被调用。<br />
最后启动runloop。</p>
<p><strong>ChatServer中的run方法</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">- (BOOL)run:(NSError **)error{<br />
&nbsp; &nbsp; BOOL successful = YES;<br />
&nbsp; &nbsp; CFSocketContext socketCtxt = {0, self, NULL, NULL, NULL};<br />
&nbsp; &nbsp; _socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;IPPROTO_TCP, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kCFSocketAcceptCallBack,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(CFSocketCallBack)&amp;amp;SocketConnectionAcceptedCallBack,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&amp;amp;socketCtxt);<br />
&nbsp; &nbsp; if (NULL == _socket) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (nil != error) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; *error = [[NSError alloc] <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; initWithDomain:ServerErrorDomain<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; code:kServerNoSocketsAvailable<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; userInfo:nil];<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; successful = NO;<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; if(YES == successful) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; // enable address reuse<br />
&nbsp; &nbsp; &nbsp; &nbsp; int yes = 1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; setsockopt(CFSocketGetNative(_socket), <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SOL_SOCKET, SO_REUSEADDR,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(void *)&amp;amp;yes, sizeof(yes));<br />
&nbsp; &nbsp; &nbsp; &nbsp; uint8_t packetSize = 128;<br />
&nbsp; &nbsp; &nbsp; &nbsp; setsockopt(CFSocketGetNative(_socket),<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SOL_SOCKET, SO_SNDBUF,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(void *)&amp;amp;packetSize, sizeof(packetSize));<br />
&nbsp; &nbsp; &nbsp; &nbsp; setsockopt(CFSocketGetNative(_socket),<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;SOL_SOCKET, SO_RCVBUF,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(void *)&amp;amp;packetSize, sizeof(packetSize));<br />
&nbsp; &nbsp; &nbsp; &nbsp; struct sockaddr_in addr4;<br />
&nbsp; &nbsp; &nbsp; &nbsp; memset(&amp;amp;addr4, 0, sizeof(addr4));<br />
&nbsp; &nbsp; &nbsp; &nbsp; addr4.sin_len = sizeof(addr4);<br />
&nbsp; &nbsp; &nbsp; &nbsp; addr4.sin_family = AF_INET;<br />
&nbsp; &nbsp; &nbsp; &nbsp; addr4.sin_port = htons(CHAT_SERVER_PORT); <br />
&nbsp; &nbsp; &nbsp; &nbsp; addr4.sin_addr.s_addr = htonl(INADDR_ANY);<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSData *address4 = [NSData dataWithBytes:&amp;amp;addr4 length:sizeof(addr4)];<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (kCFSocketSuccess != CFSocketSetAddress(_socket, (CFDataRef)address4)) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (error) *error = [[NSError alloc] <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;initWithDomain:ServerErrorDomain<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;code:kServerCouldNotBindToIPv4Address<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;userInfo:nil];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (_socket) CFRelease(_socket);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _socket = NULL;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; successful = NO;<br />
&nbsp; &nbsp; &nbsp; &nbsp; } else {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // now that the binding was successful, we get the port number <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSData *addr = [(NSData *)CFSocketCopyAddress(_socket) autorelease];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memcpy(&amp;amp;addr4, [addr bytes], [addr length]);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; self.port = ntohs(addr4.sin_port);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // 将socket 输入源加入到当前的runloop<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRunLoopRef cfrl = CFRunLoopGetCurrent();<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRunLoopSourceRef source4 = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRunLoopAddSource(cfrl, source4, kCFRunLoopDefaultMode);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRelease(source4); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //标准输入，当在命令行中输入时，回调函数便会被调用<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFFileDescriptorContext context = {0,self,NULL,NULL,NULL};<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFFileDescriptorRef stdinFDRef = CFFileDescriptorCreate(kCFAllocatorDefault, STDIN_FILENO, true, FileDescriptorCallBack, &amp;amp;context);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFFileDescriptorEnableCallBacks(stdinFDRef,kCFFileDescriptorReadCallBack);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRunLoopSourceRef stdinSource = CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, stdinFDRef, 0);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRunLoopAddSource(cfrl, stdinSource, kCFRunLoopDefaultMode);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRelease(stdinSource);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRelease(stdinFDRef); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFRunLoopRun();<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; return successful;<br />
}</div></td></tr></tbody></table></div>
</pre>
<p>当有客户端连接请求过来时， SocketConnectionAcceptedCallBack这个回调函数会被调用，根据新的全相关的socket，生成输入输出流，并设置输入输出流的delegate方法，将其添加到当前的runloop，这样流中有数据过来的时候，delegate方法会被调用。</p>
<p><strong>SocketConnectionAcceptedCallBack函数</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">static void SocketConnectionAcceptedCallBack(CFSocketRef socket, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CFSocketCallBackType type, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CFDataRef address, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;const void *data, void *info) {<br />
&nbsp; &nbsp; ChatServer *theChatServer = (ChatServer *)info;<br />
&nbsp; &nbsp; if (kCFSocketAcceptCallBack == type) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; // 摘自kCFSocketAcceptCallBack的文档，New connections will be automatically accepted and the callback is called with the data argument being a pointer to a CFSocketNativeHandle of the child socket. This callback is usable only with listening sockets.<br />
&nbsp; &nbsp; &nbsp; &nbsp; CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;<br />
&nbsp; &nbsp; &nbsp; &nbsp; // create the read and write streams for the connection to the other process<br />
&nbsp; &nbsp; &nbsp; &nbsp; CFReadStreamRef readStream = NULL;<br />
&nbsp; &nbsp; &nbsp; &nbsp; CFWriteStreamRef writeStream = NULL;<br />
&nbsp; &nbsp; &nbsp; &nbsp; CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&amp;amp;readStream, &amp;amp;writeStream);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if(NULL != readStream &amp;amp;&amp;amp; NULL != writeStream) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFReadStreamSetProperty(readStream, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; kCFStreamPropertyShouldCloseNativeSocket,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; kCFBooleanTrue);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CFWriteStreamSetProperty(writeStream, <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kCFStreamPropertyShouldCloseNativeSocket,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kCFBooleanTrue);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSInputStream *inputStream = (NSInputStream *)readStream;//toll-free bridging<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSOutputStream *outputStream = (NSOutputStream *)writeStream;//toll-free bridging<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; inputStream.delegate = theChatServer;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [inputStream open];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; outputStream.delegate = theChatServer;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [outputStream open];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Client *aClient = [[Client alloc] init];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; aClient.inputStream = inputStream;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; aClient.outputStream = outputStream;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; aClient.sock_fd = nativeSocketHandle;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [theChatServer.clients setValue:aClient &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;forKey:[NSString stringWithFormat:@&quot;%d&quot;,inputStream]];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;有新客户端(sock_fd=%d)加入&quot;,nativeSocketHandle);<br />
&nbsp; &nbsp; &nbsp; &nbsp; } else {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; close(nativeSocketHandle);<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (readStream) CFRelease(readStream);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (writeStream) CFRelease(writeStream);<br />
&nbsp; &nbsp; }<br />
}</div></td></tr></tbody></table></div>
</pre>
<p>当客户端有数据传过来时，相应的NSInputStream的delegate方法被调用</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; - (void) stream:(NSStream*)stream handleEvent:(NSStreamEvent)eventCode {<br />
&nbsp; &nbsp; switch (eventCode) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; case NSStreamEventOpenCompleted: {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; case NSStreamEventHasBytesAvailable: {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Client *client = [self.clients objectForKey:[NSString stringWithFormat:@&quot;%d&quot;,stream]];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSMutableData *data = [NSMutableData data];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; uint8_t *buf = calloc(128, sizeof(uint8_t));<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSUInteger len = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while([(NSInputStream*)stream hasBytesAvailable]) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; len = [(NSInputStream*)stream read:buf maxLength:128];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(len &amp;gt; 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [data appendBytes:buf length:len];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; free(buf);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ([data length] == 0) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //客户端退出<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;客户端(sock_fd=%d)退出&quot;,client.sock_fd);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [self.clients removeObjectForKey:[NSString stringWithFormat:@&quot;%d&quot;,stream]];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; close(client.sock_fd);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }else{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;收到客户端(sock_fd=%d)消息:%@&quot;,client.sock_fd,[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; case NSStreamEventHasSpaceAvailable: {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; case NSStreamEventEndEncountered: {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; case NSStreamEventErrorOccurred: {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; default:<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; }<br />
}</div></td></tr></tbody></table></div>
</pre>
<p>当在debug窗口中输入内容并回车时，标准输入缓冲区中便有数据了，这个时候回调函数FileDescriptorCallBack将被调用，处理标准输入。</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">static void FileDescriptorCallBack(CFFileDescriptorRef f,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;CFOptionFlags callBackTypes,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;void *info){<br />
&nbsp; &nbsp; int fd = CFFileDescriptorGetNativeDescriptor(f);<br />
&nbsp; &nbsp; ChatServer *theChatServer = (ChatServer *)info;<br />
&nbsp; &nbsp; if (fd == STDIN_FILENO) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSData *inputData = [[NSFileHandle fileHandleWithStandardInput] availableData];<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSString *inputString = [[[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding] autorelease];<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;准备发送消息:%@&quot;,inputString);<br />
&nbsp; &nbsp; &nbsp; &nbsp; for (Client *client in [theChatServer.clients allValues]) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [client.outputStream write:[inputData bytes] maxLength:[inputData length]];<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; //处理完数据之后必须重新Enable 回调函数<br />
&nbsp; &nbsp; &nbsp; &nbsp; CFFileDescriptorEnableCallBacks(f,kCFFileDescriptorReadCallBack);<br />
&nbsp; &nbsp; }<br />
}</div></td></tr></tbody></table></div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2012/01/macios-socket/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iOS持久化</title>
		<link>http://geeklu.com/2012/01/ios-persistence/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=ios-persistence</link>
		<comments>http://geeklu.com/2012/01/ios-persistence/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 08:46:47 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>
		<category><![CDATA[sqlite]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31110</guid>
		<description><![CDATA[文件系统 归档和序列化 数据库 1.文件系统 不管是Mac OS X 还是iOS的文件系统都是建立在UNIX文件系统基础之上的。 1.1 沙盒模型 在iOS中，一个App的读写权限只局限于自己的沙盒目录中。 沙盒模型到底有哪些好处呢? 安全：别的App无法修改你的程序或数据 保护隐私：别的App无法读取你的程序和数据 方便删除：因为一个App所有产生的内容都在自己的沙盒中，所以删除App只需要将沙盒删除就可以彻底删除程序了 iOS App沙盒中的目录 目录 说明 App Bundle 如xxx.app 其实是一个目录，里面有app本身的二进制数据以及资源文件 Documents 存放程序产生的文档数据 Library 下面默认包含下面两个目录 Caches Preferences tmp 临时文件目录 如果我们想在程序中获取上面某个目录的路径，应该如何实现呢？ 下面就讲讲路径的获取， 通过 1NSPathUtilities.h 中的 1NSSearchPathForDirectoriesInDomains 函数，我们便可以获取我们想要的路径。 此函数具体声明如下: NSArray *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde); directory 目录类型 比如Documents目录 就是NSDocumentDirectory domainMask 在iOS的程序中这个取NSUserDomainMask expandTilde YES，表示将~展开成完整路径 注意函数返回的类型为数组，在iOS中一般这个数组中只包含一个元素，所以直接取lastObject即可。 [...]]]></description>
			<content:encoded><![CDATA[<ul>
<li>文件系统</li>
<li>归档和序列化</li>
<li>数据库</li>
</ul>
<hr />
<span id="1."><h2>1.文件系统</h2></span>
<p>不管是Mac OS X 还是iOS的文件系统都是建立在UNIX文件系统基础之上的。</p>
<span id="1.1_"><h3>1.1 沙盒模型</h3></span>
<p>在iOS中，一个App的读写权限只局限于自己的沙盒目录中。</p>
<blockquote>
<p><strong>沙盒模型到底有哪些好处呢?</strong> <br />
安全：别的App无法修改你的程序或数据 <br />
保护隐私：别的App无法读取你的程序和数据 <br />
方便删除：因为一个App所有产生的内容都在自己的沙盒中，所以删除App只需要将沙盒删除就可以彻底删除程序了</p>
</blockquote>
<p><span id="more-31110"></span></p>
<p>iOS App沙盒中的目录</p>
<table>
<col />
<col />
<thead>
<tr>
<th>目录</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>App Bundle</td>
<td>如xxx.app 其实是一个目录，里面有app本身的二进制数据以及资源文件</td>
</tr>
<tr>
<td>Documents</td>
<td>存放程序产生的文档数据</td>
</tr>
<tr>
<td>Library</td>
<td>下面默认包含下面两个目录 Caches Preferences</td>
</tr>
<tr>
<td>tmp</td>
<td>临时文件目录</td>
</tr>
</tbody>
</table>
<p>如果我们想在程序中获取上面某个目录的路径，应该如何实现呢？<br />
下面就讲讲路径的获取，<br />
通过</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NSPathUtilities.h</div></td></tr></tbody></table></div>
<p>中的</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NSSearchPathForDirectoriesInDomains</div></td></tr></tbody></table></div>
<p>函数，我们便可以获取我们想要的路径。<br />
此函数具体声明如下:</p>
<blockquote>
<p>NSArray *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde); <br />
<strong>directory</strong> 目录类型 比如Documents目录 就是NSDocumentDirectory <br />
<strong>domainMask</strong> 在iOS的程序中这个取NSUserDomainMask <br />
<strong>expandTilde</strong> YES，表示将~展开成完整路径</p>
</blockquote>
<p>注意函数返回的类型为数组，在iOS中一般这个数组中只包含一个元素，所以直接取lastObject即可。</p>
<h3 id="nsfilemanager">1.2 NSFileManager</h3>
<p>NSFileManager提供一个类方法获得一个单例。</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">/* Returns the default singleton instance.*/<br />
+ (NSFileManager *)defaultManager;</div></td></tr></tbody></table></div>
</pre>
<p>下面罗列了NSFileManager的常用方法</p>
<ul>
<li>
<p>新建目录  </p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error;</div></td></tr></tbody></table></div>
</pre>
<p>createIntermediates这个参数一般为YES，表示如果目录路径中间的某个目录不存在则创建之,如果是NO的话，则要保证所创建目录的父目录都必须已经存在</p>
</li>
<li>
<p>获取目录下的所有文件</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error;</div></td></tr></tbody></table></div>
</pre>
<p>如果目录为空，则返回空数组</p>
</li>
<li>
<p>其他的一些方法</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;<br />
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;<br />
- (BOOL)linkItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;<br />
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;</div></td></tr></tbody></table></div>
</pre>
</li>
</ul>
<p>更多的可以查看文档 <a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/Reference/Reference.html">NSFileManager Class Reference</a>。</p>
<p>在实际项目中，我们一般会写一个工具类来负责项目中所有的路径操作。</p>
<h2 id="archivesserializations">2. 归档（Archives） 和 序列化（Serializations）</h2>
<p>我们经常听到“序列化”，“反序列化”这样的字眼，其实“序列化”的意思就是将对象转换成字节流以便保存或传输，“反序列化”便是一个相反的过程，从字节流转到对象。   </p>
<p>在这节中涉及到一种文件类型plist，plist就是Property List 的缩写,即所谓的属性列表，属性列表有两种数据格式，一种是XML的，方便阅读和编辑；另一种是二进制的，节省存储空间，以及提高效率。</p>
<p>在Objective-C中这个对象和字节流的互转分成两类:   </p>
<ul>
<li><strong>归档</strong> 普通自定义对象和字节流之间的转换</li>
<li><strong>序列化</strong> 某些特定类型（NSDictionary, NSArray, NSString, NSDate, NSNumber，NSData）的数据和字节流之间(通常将其保存为plist文件)的转换</li>
</ul>
<p>不过本质上讲上述两种都是对象图(<a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Archiving/Articles/objectgraphs.html#//apple_ref/doc/uid/20001293-CJBDFIBI">Object Graph</a>)和字节流之间的转换.<br />
Apple关于序列化和归档的编程指南: <a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Archiving/Archiving.html">Archives and Serializations Programming Guide</a> 。</p>
<span id="2.1_"><h3>2.1 归档</h3></span>
<p>如果我们需要将自定义的一个对象保存到文件，应该如何做呢？ <br />
这里引入两个东西：一个是NSCoding协议 ；另一个是NSKeyedArchiver，NSKeyedArchiver其实继承于NSCoder，可以以键值对的方式将对象的属性进行序列化和反序列化。 <br />
具体的过程可以这样描述 <strong>通过NSKeyedArchiver 可以将实现了NSCoding协议的对象 和 字节流 相互转换</strong> 。   </p>
<p>像一些框架中的数据类型如NSDictionary,NSArray,NSString&#8230; 都已经实现了NSCoding协议，所以可以直接对他们进行归档操作。   </p>
<p>这里来一个比较完整的例子，一个Address类，一个User类，User类下有个Address类型的属性。</p>
<p><strong>Address类</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">@interface Address : NSObject&amp;lt;NSCoding&amp;gt;{<br />
&nbsp; &nbsp; NSString *country;<br />
&nbsp; &nbsp; NSString *city;<br />
}<br />
@property(nonatomic,copy) NSString *country;<br />
@property(nonatomic,copy) NSString *city;<br />
@end<br />
//////////////////////////////////////////////////////<br />
#import &quot;Address.h&quot;<br />
<br />
@implementation Address<br />
@synthesize country;<br />
@synthesize city;<br />
<br />
- (void)encodeWithCoder:(NSCoder *)aCoder{<br />
&nbsp; &nbsp; [aCoder encodeObject:country forKey:@&quot;country&quot;];<br />
&nbsp; &nbsp; [aCoder encodeObject:city forKey:@&quot;city&quot;];<br />
}<br />
<br />
- (id)initWithCoder:(NSCoder *)aDecoder{<br />
&nbsp; &nbsp; if (self = [super init]) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; [self setCountry:[aDecoder decodeObjectForKey:@&quot;country&quot;]];<br />
&nbsp; &nbsp; &nbsp; &nbsp; [self setCity:[aDecoder decodeObjectForKey:@&quot;city&quot;]];<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; return self;<br />
}<br />
<br />
@end</div></td></tr></tbody></table></div>
</pre>
<p><strong>User类</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">#import &amp;lt;Foundation/Foundation.h&amp;gt;<br />
#import &quot;Address.h&quot;<br />
@interface User : NSObject&amp;lt;NSCoding&amp;gt;{<br />
&nbsp; &nbsp; NSString *_name;<br />
&nbsp; &nbsp; NSString *_password;<br />
<br />
&nbsp; &nbsp; Address *_address;<br />
}<br />
@property(nonatomic,copy) NSString *name;<br />
@property(nonatomic,copy) NSString *password;<br />
@property(nonatomic,retain) Address *address;<br />
<br />
@end<br />
/////////////////////////////////////////////////////////<br />
#import &quot;User.h&quot;<br />
<br />
@implementation User<br />
@synthesize name = _name;<br />
@synthesize password = _password;<br />
@synthesize address = _address;<br />
<br />
- (void)encodeWithCoder:(NSCoder *)aCoder{<br />
&nbsp; &nbsp; [aCoder encodeObject:_name forKey:@&quot;name&quot;];<br />
&nbsp; &nbsp; [aCoder encodeObject:_password forKey:@&quot;password&quot;];<br />
&nbsp; &nbsp; [aCoder encodeObject:_address forKey:@&quot;address&quot;];<br />
}<br />
- (id)initWithCoder:(NSCoder *)aDecoder{<br />
&nbsp; &nbsp; if (self = [super init]) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; [self setName:[aDecoder decodeObjectForKey:@&quot;name&quot;]];<br />
&nbsp; &nbsp; &nbsp; &nbsp; [self setPassword:[aDecoder decodeObjectForKey:@&quot;password&quot;]];<br />
&nbsp; &nbsp; &nbsp; &nbsp; [self setAddress:[aDecoder decodeObjectForKey:@&quot;address&quot;]];<br />
&nbsp; &nbsp; }<br />
&nbsp; &nbsp; return self;<br />
}<br />
@end</div></td></tr></tbody></table></div>
</pre>
<p><strong>使用示例</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Address *myAddress = [[[Address alloc] init] autorelease];<br />
myAddress.country = @&quot;中国&quot;;<br />
myAddress.city = @&quot;杭州&quot;;<br />
<br />
User *user = [[[User alloc] init] autorelease];<br />
user.name = @&quot;卢克&quot;;<br />
user.password = @&quot;lukejin&quot;;<br />
user.address = myAddress;<br />
<br />
[NSKeyedArchiver archiveRootObject:user toFile:@&quot;/Users/Luke/Desktop/user&quot;];<br />
<br />
id object = [NSKeyedUnarchiver unarchiveObjectWithFile:@&quot;/Users/Luke/Desktop/user&quot;];<br />
NSLog(@&quot;Object Class : %@&quot;,[object class]);</div></td></tr></tbody></table></div>
</pre>
<p>通过查看文件内容可以发现，保存的是plist的二进制数据格式。<br />
转成XML可以看到如下内容:</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br />49<br />50<br />51<br />52<br />53<br />54<br />55<br />56<br />57<br />58<br />59<br />60<br />61<br />62<br />63<br />64<br />65<br />66<br />67<br />68<br />69<br />70<br />71<br />72<br />73<br />74<br />75<br />76<br />77<br />78<br />79<br />80<br />81<br />82<br />83<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br />
&nbsp; &nbsp; &lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/&nbsp; &nbsp; PropertyList-1.0.dtd&quot;&gt;<br />
&nbsp; &nbsp; &lt;plist version=&quot;1.0&quot;&gt;<br />
&nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$archiver&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;NSKeyedArchiver&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$objects&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;array&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;$null&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$class&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;8&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;address&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;4&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;name&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;2&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;password&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;3&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;卢克&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;lukejin&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$class&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;7&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;city&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;6&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;country&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;5&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;中国&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;杭州&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$classes&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;array&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;Address&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;NSObject&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/array&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$classname&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;Address&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$classes&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;array&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;User&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;NSObject&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/array&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$classname&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;User&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/array&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$top&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;root&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;CF$UID&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;1&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;$version&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;100000&lt;/integer&gt;<br />
&nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &lt;/plist&gt;</div></td></tr></tbody></table></div>
</pre>
<span id="2.2_"><h3>2.2 序列化</h3></span>
<p>在实际的项目中，我们一般是将NSDictionary或NSArray的对象保存到文件或者从文件读取成对象。<br />
当然这种只是适用于数据量不是很大的应用场景。<br />
NSDictionary和NSArray 都有一个写入文件的方法</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; - (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;</div></td></tr></tbody></table></div>
</pre>
<p>NSDictionary和NSArray会直接写成plist文件。</p>
<span id="2.2.1_"><h4>2.2.1 序列化的方式</h4></span>
<p>序列化可以通过两种途径来进行</p>
<span id=""><h5>使用数据对象自带的方法</h5></span>
<p>写文件</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp;NSMutableDictionary *dataDictionary = [[[NSMutableDictionary alloc] init] autorelease];<br />
&nbsp; &nbsp; [dataDictionary setValue:[NSNumber numberWithInt:222] forKey:@&quot;intNumber&quot;];<br />
&nbsp; &nbsp; [dataDictionary setValue:[NSArray arrayWithObjects:@&quot;1&quot;,@&quot;2&quot;, nil] forKey:@&quot;testArray&quot;];<br />
&nbsp; &nbsp; [dataDictionary writeToFile:@&quot;/Users/Luke/Desktop/test.plist&quot; atomically:YES];</div></td></tr></tbody></table></div>
</pre>
<p>写完的文件内容如下:</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;<br />
&nbsp; &nbsp; &lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;<br />
&nbsp; &nbsp; &lt;plist version=&quot;1.0&quot;&gt;<br />
&nbsp; &nbsp; &lt;dict&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;intNumber&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;integer&gt;222&lt;/integer&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;key&gt;testArray&lt;/key&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;array&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;1&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;string&gt;2&lt;/string&gt;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &lt;/array&gt;<br />
&nbsp; &nbsp; &lt;/dict&gt;<br />
&nbsp; &nbsp; &lt;/plist&gt;</div></td></tr></tbody></table></div>
</pre>
<p>从文件读取</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NSDictionary *dictionaryFromFile = [NSDictionary dictionaryWithContentsOfFile:@&quot;/Users/Luke/Desktop/test.plist&quot;];</div></td></tr></tbody></table></div>
</pre>
<h5 id="nspropertylistserialization">使用NSPropertyListSerialization类</h5>
<p>通过NSPropertyListSerialization类可以将数据对象直接转成NSData或者直接写到文件或者流中去.</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NSMutableDictionary *dataDictionary = [[[NSMutableDictionary alloc] init] autorelease];<br />
[dataDictionary setValue:[NSNumber numberWithInt:222] forKey:@&quot;intNumber&quot;];<br />
[dataDictionary setValue:[NSArray arrayWithObjects:@&quot;1&quot;,@&quot;2&quot;, nil] forKey:@&quot;testArray&quot;];<br />
<br />
NSString *error;<br />
NSData *xmlData = [NSPropertyListSerialization dataFromPropertyList:dataDictionary<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;format:NSPropertyListXMLFormat_v1_0<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;errorDescription:&amp;amp;error];<br />
if(xmlData) {<br />
&nbsp; &nbsp; NSLog(@&quot;No error creating XML data.&quot;);<br />
&nbsp; &nbsp; [xmlData writeToFile:@&quot;/Users/Luke/Desktop/test2.plist&quot; atomically:YES];<br />
}<br />
else {<br />
&nbsp; &nbsp; if (error) {<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;error:%@&quot;, error);<br />
&nbsp; &nbsp; &nbsp; &nbsp; [error release];<br />
&nbsp; &nbsp; }<br />
}</div></td></tr></tbody></table></div>
</pre>
<p>读取</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NSDictionary *dictionaryFromFile = (NSDictionary *)[NSPropertyListSerialization <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;propertyListWithData:[NSData dataWithContentsOfFile:@&quot;/Users/Luke/Desktop/test2.plist&quot;] <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; options:0<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; format:NULL<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; error:&amp;amp;error];</div></td></tr></tbody></table></div>
</pre>
<h4 id="userdefaults">2.2.2 User Defaults</h4>
<p>User Defaults 顾名思义就是一个用户为系统以及程序设置的默认值。每个用户都有自己的一套数据，用户和用户之间没法共享的。</p>
<p>我们都知道每一个程序都会保存一些设置数据，比如记住上次窗口的位置和大小，记住是否弹出某些提示信息等。苹果提供了一个统一的解决方案，就是每一个app都有一个plist文件专门用以保存偏好设置数据。<br />
plist文件名默认是程序Bundle identifier,扩展名为plist.</p>
<p>除了程序自己的设置外，系统还有一些全局的或者其它的一些设置，也属于User Defaults的范畴，User Defaults的持久化数据都保存在</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">~/Library/Preferences</div></td></tr></tbody></table></div>
<p>目录中.</p>
<p>这里有一点简要的说一下，User Defaults  中存放的key value分放在多个Domain中，取的时候按一定的次序取查找，次序如下:</p>
<ul>
<li><strong>The Argument Domain</strong>   程序启动的时候以参数的方式传入的</li>
<li><strong>The Application Domain</strong> 通过NSUserDefaults往里面写数据的时候默认就是写到这个Domain的，通过Bundle identifier来标识</li>
<li><strong>The Global Domain</strong> 用户的全局的设置（系统的偏好设置）会放在这个Domain下，比如用户的语言设置，滚动条的设置等，里面的设置会对所有的程序起作用。</li>
<li><strong>The Languages Domains</strong></li>
<li><strong>The Registration Domain</strong>  这个domain里面的key value是提供默认值的，一般会在程序启动的设置进行设置，他们都不会被持久化到文件的。当某个key对应的值在上面的那些domain中都不存在的时候，就到这里找。</li>
</ul>
<p>Mac系统还为user defaults提供了很好的命令行工具，</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">defaults</div></td></tr></tbody></table></div>
<p>你可以通过下面的方式查看具体使用方式</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">man defaults</div></td></tr></tbody></table></div>
</pre>
<p>可以通过</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">defaults domains</div></td></tr></tbody></table></div>
<p>查看当前用户的所有的domain，通过</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">defaults read NSGlobalDomain</div></td></tr></tbody></table></div>
<p>读取 <strong>The Global Domain</strong> 中的所有值。</p>
<p><strong>NSUserDefaults</strong> 类来读写Preferences设置，而无需考虑文件位置等细节问题。</p>
<p><strong>NSUserDefaults</strong> 用起来和 <strong>NSDictionary</strong> 很相似，多了一个Domain的概念在里面。<br />
<strong>NSUserDefaults</strong> 一样提供了一个获取单例的方法.</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">+ (NSUserDefaults *)standardUserDefaults</div></td></tr></tbody></table></div>
</pre>
<p>NSUserDefaults提供了一系列的接口来根据key获取对应的value，搜索的次序按照上面提及到的次序在各个Domain中进行查找。还提供了一系列的 Setting Default Values的方法，这些设置的值都是在 <strong>The Application Domain</strong> 下的.当然也提供了修改其他Domain下的值的方法，只是需要整体的设置。</p>
<span id="3."><h2>3.数据库</h2></span>
<p>Mac上自带安装了SQLite3 ,如果你之前接触过关系型数据库，你可以通过命令行来对SQLite进行初步的认识</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ sqlite3 test.db<br />
SQLite version 3.7.5<br />
Enter &quot;.help&quot; for instructions<br />
Enter SQL statements terminated with a &quot;;&quot;<br />
sqlite&amp;gt;create table if not exists names(id integer primary key asc, name text); <br />
sqlite&amp;gt; insert into names(name) values('Luke');<br />
sqlite&amp;gt; select * from names;<br />
1|Luke<br />
sqlite&amp;gt;</div></td></tr></tbody></table></div>
</pre>
<p>那如果在代码中使用SQLite呢？</p>
<ul>
<li>添加sqlite的动态链接库 libsqlite3.0.dylib</li>
<li>引入头文件 #import &#8220;sqlite3.h&#8221;</li>
</ul>
<p>这样之后你便可以通过C的接口来操作数据库了</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br />33<br />34<br />35<br />36<br />37<br />38<br />39<br />40<br />41<br />42<br />43<br />44<br />45<br />46<br />47<br />48<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">sqlite3 *database;//sqlite3的类型其实只是一个结构体struct<br />
NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , NSUserDomainMask <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , YES); <br />
NSString *databaseFilePath=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:@&quot;luke.db&quot;];<br />
<br />
//打开数据库<br />
if (sqlite3_open([databaseFilePath UTF8String], &amp;amp;database)==SQLITE_OK) { <br />
&nbsp; &nbsp;NSLog(@&quot;open sqlite db ok.&quot;); <br />
&nbsp; &nbsp; char *errorMsg; <br />
&nbsp; &nbsp; const char *createSql=&quot;create table if not exists names (id integer primary key asc,name text)&quot;;<br />
&nbsp; &nbsp; //创建表<br />
&nbsp; &nbsp; if (sqlite3_exec(database, createSql, NULL, NULL, &amp;amp;errorMsg)==SQLITE_OK) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;create ok.&quot;); <br />
&nbsp; &nbsp; }else {<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;error: %s&quot;,errorMsg); <br />
&nbsp; &nbsp; &nbsp; &nbsp; sqlite3_free(errorMsg);<br />
&nbsp; &nbsp; }<br />
<br />
<br />
&nbsp; &nbsp; //插入数据<br />
&nbsp; &nbsp; const char *insertSql=&quot;insert into names (name) values(\&quot;Luke\&quot;)&quot;; <br />
&nbsp; &nbsp; if (sqlite3_exec(database, insertSql, NULL, NULL, &amp;amp;errorMsg) == SQLITE_OK) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;insert ok.&quot;); <br />
&nbsp; &nbsp; }else {<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;error: %s&quot;,errorMsg); <br />
&nbsp; &nbsp; &nbsp; &nbsp; sqlite3_free(errorMsg); <br />
&nbsp; &nbsp; }<br />
<br />
<br />
&nbsp; &nbsp; const char *selectSql=&quot;select id,name from names&quot;; <br />
&nbsp; &nbsp; sqlite3_stmt *statement; <br />
&nbsp; &nbsp; if (sqlite3_prepare_v2(database, selectSql, -1, &amp;amp;statement, nil) == SQLITE_OK) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;select ok.&quot;);<br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; while (sqlite3_step(statement)==SQLITE_ROW) { <br />
&nbsp; &nbsp; &nbsp; &nbsp; int _id=sqlite3_column_int(statement, 0); <br />
&nbsp; &nbsp; &nbsp; &nbsp; char *name=(char *)sqlite3_column_text(statement, 1); <br />
&nbsp; &nbsp; &nbsp; &nbsp; NSString *nameString = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];<br />
&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;row&amp;gt;&amp;gt;id %i, name %@&quot;,_id,nameString); <br />
&nbsp; &nbsp; }<br />
<br />
&nbsp; &nbsp; sqlite3_finalize(statement);<br />
<br />
}<br />
<br />
sqlite3_close(database);</div></td></tr></tbody></table></div>
</pre>
<p>你会发现这完全是C语言编程，和Objective-C的代码混在一起格格不入，也很不方便，所以便有人开发了开源的sqlite c接口的wrapper</p>
<ul>
<li>FMDB <a href="https://github.com/ccgus/fmdb">https://github.com/ccgus/fmdb</a></li>
<li>EGODatabase  <a href="https://github.com/ccgus/fmdb">https://github.com/enormego/egodatabase</a> (部分代码来自FMDB，thread safe)</li>
</ul>
<p>具体的使用方法，各自的文档都写的比较清楚。<br />
FMDB不支持多线程同时使用同一个数据库连接进行操作，否则会有线程安全问题，有可能导致数据库文件损坏。EGODatabase则引入了多线程的支持，部分代码借鉴了FMDB，两者在使用上非常的相似。另EGODatabase提供了异步数据库操作的支持，将数据库操作封装成数据库请求（其继承于NSOperation），数据库请求创建好了，丢到一个OperationQueue中被异步的进行执行，当请求数据完成之后 ，相应的delegate方法会被调用，然后你可以在主线程更新显示了.</p>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2012/01/ios-persistence/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Blocks编程</title>
		<link>http://geeklu.com/2012/01/block/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=block</link>
		<comments>http://geeklu.com/2012/01/block/#comments</comments>
		<pubDate>Thu, 19 Jan 2012 08:44:19 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>
		<category><![CDATA[block]]></category>
		<category><![CDATA[c]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31109</guid>
		<description><![CDATA[介绍 声明创建和调用 Block和变量 Block实际应用 1.介绍 Block是一个C Level的语法以及运行时的一个特性,非常像标准C中的函数(函数指针)，但是其运行需要编译器和运行时支持,目前LLVM+Clang可以很好的支持Block(苹果修改过的GCC也可以)。Block和函数不同的是其语义 闭包 特性，以及可以有匿名block的存在。 你可以在LLVM的官方网站查看Block语言规范. 你可以通过 1^ 运算符来声明一个block变量，或用来表明block定义的开始，而block的代码块则是包含在一对花括号 1{} 内的. 12345int multiplier = 2; &#160; int (^myBlock)(int) = ^(int num){ &#160; &#160; &#160; return num * multiplier; &#160; }; &#160; printf(&#34;%d&#34;,myBlock(4)); 上面代码中的 1myBlock 就是Block的变量名,由myBlock变量的声明可以看出，它返回值为int类型，且存在一个int型的参数。 等于号后面就是Block的定义并将其赋值给myBlock . Block的调用就和C函数的使用类似. 2. 声明创建和调用 声明Block变量 Block变量保存着指向Block的指针，声明一个Block变量就和声明一个函数指针变量类似，只是将*改成了^. 其他的就和C的类型系统保持一致了。 123void (^blockReturningVoidWithVoidArgument)(void); &#160; int (^blockReturningIntWithIntAndCharArguments)(int, char); &#160; void [...]]]></description>
			<content:encoded><![CDATA[<ul>
<li>介绍</li>
<li>声明创建和调用</li>
<li>Block和变量</li>
<li>Block实际应用</li>
</ul>
<hr />
<span id="1."><h3>1.介绍</h3></span>
<p>Block是一个C Level的语法以及运行时的一个特性,非常像标准C中的函数(函数指针)，但是其运行需要编译器和运行时支持,目前LLVM+Clang可以很好的支持Block(苹果修改过的GCC也可以)。Block和函数不同的是其语义 <strong>闭包</strong> 特性，以及可以有匿名block的存在。<br />
你可以在LLVM的官方网站查看<a href="http://clang.llvm.org/docs/BlockLanguageSpec.txt">Block语言规范</a>. <br />
你可以通过</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">^</div></td></tr></tbody></table></div>
<p>运算符来声明一个block变量，或用来表明block定义的开始，而block的代码块则是包含在一对花括号</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">{}</div></td></tr></tbody></table></div>
<p>内的.   </p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">int multiplier = 2; &nbsp; <br />
int (^myBlock)(int) = ^(int num){ &nbsp; <br />
&nbsp; &nbsp; return num * multiplier; &nbsp; <br />
}; &nbsp; <br />
printf(&quot;%d&quot;,myBlock(4));</div></td></tr></tbody></table></div>
</pre>
<p>上面代码中的</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">myBlock</div></td></tr></tbody></table></div>
<p>就是Block的变量名,由myBlock变量的声明可以看出，它返回值为int类型，且存在一个int型的参数。<br />
等于号后面就是Block的定义并将其赋值给myBlock .<br />
Block的调用就和C函数的使用类似.</p>
<span id="2._"><h3>2. 声明创建和调用</h3></span>
<p><span id="more-31109"></span></p>
<p><strong>声明Block变量</strong> <br />
Block变量保存着指向Block的指针，声明一个Block变量就和声明一个函数指针变量类似，只是将*改成了^.<br />
其他的就和C的类型系统保持一致了。</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void (^blockReturningVoidWithVoidArgument)(void); &nbsp; <br />
int (^blockReturningIntWithIntAndCharArguments)(int, char); &nbsp; <br />
void (^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int);</div></td></tr></tbody></table></div>
</pre>
<p>另 Block还支持可变参数variadic (&#8230;) ，没有参数的话，变量列表的地方必须写上void关键字.</p>
<p><strong>声明Block类型</strong><br />
你可以通过typedef声明Block的类型，这样多个地方需要使用同种类型的Block的时候会比较方便,</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">typedef float (^MyBlockType)(float, float); &nbsp; <br />
MyBlockType myFirstBlock = // ... ; &nbsp; <br />
MyBlockType mySecondBlock = // ... ;</div></td></tr></tbody></table></div>
</pre>
<p><strong>Block创建</strong><br />
声明了一个Block变量之后，可以为这个变量赋值 <br />
    blockReturningVoidWithVoidArgument = ^{ <br />
          printf(&#8220;%s&#8221;,&#8221;Block Returing Void With Void Argument.&#8221;); <br />
    }</p>
<p>当然你可以在声明变量的同时赋值</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void (^blockReturningVoidWithVoidArgument)(void) = ^{ &nbsp; <br />
&nbsp; &nbsp; &nbsp; printf(&quot;%s&quot;,&quot;Block Returing Void With Void Argument.&quot;); &nbsp; <br />
}</div></td></tr></tbody></table></div>
</pre>
<p><strong>Block调用</strong><br />
Block的调用和函数的调用是非常相似的,如上面定义的blockReturningVoidWithVoidArgument，调用的时候则直接</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">blockReturningVoidWithVoidArgument();</div></td></tr></tbody></table></div>
<p>便可.   </p>
<p><strong>匿名Block</strong><br />
当一个Block作为函数参数的时候，一般实参都是以匿名Block的方式传过去的。</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">void callVoidVoid(void (^closure)(void)) { &nbsp; <br />
&nbsp; &nbsp;closure(); &nbsp; <br />
} &nbsp; <br />
int main(int argc, char *argv[]) { &nbsp; <br />
&nbsp; &nbsp;__block int i = 10; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp;callVoidVoid(^{ ++i; }); &nbsp; <br />
&nbsp; &nbsp;if (i != 11) { &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp;printf(&quot;*** %s didn't update i\n&quot;, argv[0]); &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp;return 1; &nbsp; <br />
&nbsp; &nbsp;} &nbsp; <br />
&nbsp; &nbsp;printf(&quot;%s: success\n&quot;, argv[0]); &nbsp; <br />
&nbsp; &nbsp;return 0; &nbsp; <br />
}</div></td></tr></tbody></table></div>
</pre>
<p>当然你也可以直接调用匿名Block，如</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">^{ ++i; }();</div></td></tr></tbody></table></div>
</pre>
<h3 id="block">3.Block和变量</h3>
<p>一个Block的内部是可以引用自身作用域外的变量的，包括static变量，extern变量或自由变量（定义一个变量的时候，如果不加存储修饰符，默认情况下就是自由变量auto,auto变量保存在stack中的.除了auto之外还存在register，static等存储修饰符） ,对于自由变量，在Block中是只读的。在引入block的同时，还引入了一种特殊的变量存储修饰符</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">__block</div></td></tr></tbody></table></div>
<p>,通过它的变量叫做block变量，block变量在block内部可以进行写操作的。这些变量中，自由变量是最特殊的，在Block声明的时候，自由变量在Block内部只读且其值被固定住（自由变量被拷贝了一份，且限定为const），即使在block调用前改变了这个自由变量的值，block调用的时候，看到的却还是block声明的时候的那个值。</p>
<p><strong>代码示例 3.1</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; __block int blockValue = 0;<br />
&nbsp; &nbsp; int autoValue = 0;<br />
<br />
&nbsp; &nbsp; void(^printValue)(void) = ^{<br />
&nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;blockValue = %d\n&quot;,blockValue);<br />
&nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;autoValue = %d\n&quot;,autoValue);<br />
<br />
&nbsp; &nbsp; };<br />
&nbsp; &nbsp; blockValue ++;<br />
&nbsp; &nbsp; autoValue ++;<br />
&nbsp; &nbsp; printValue();</div></td></tr></tbody></table></div>
</pre>
<p>3.1中的代码，输出的值为</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">blockValue = 1<br />
autoValue = 0</div></td></tr></tbody></table></div>
</pre>
<p>可以看到自由变量尽管自增了，但是在调用printValue这个Block的时候，看到的还是其定义的时候看到的那个autoValue的值,autoValue的值在Block的内部示无法修改的，要不然编译器会报错:</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Semantic Issue: Variable is not assignable (missing __block type specifier)</div></td></tr></tbody></table></div>
</pre>
<p>Block定义时内存是分配在stack上的，当其作用域结束，就会被自动释放，所以你不需要去担心它的内存情况，我们可以对一个Block进行</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Block_copy()</div></td></tr></tbody></table></div>
<p>操作，</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Block_copy()</div></td></tr></tbody></table></div>
<p>之后，Block会被拷贝到heap中的内存中，且其所有的引用到的自由变量也将会被拷贝，当然你得记得通过</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Block_release()</div></td></tr></tbody></table></div>
<p>释放heap的内存空间哦。<br />
在objc中Block是和对象一样被看作一等公民的（其实这是objc的Block扩展的功劳），你可以像使用对象那样对Block进行retain（retain只对heap中的Block起作用）,copy以及release操作.</p>
<p>在Block内部如果引用到对象或者对象的成员变量，那么当Block被拷贝</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Block_copy()</div></td></tr></tbody></table></div>
<p>之后，这个对象的引用计数会增加。</p>
<p><strong>代码示例 3.2</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; NSObject *testObject = [[NSObject alloc] init];<br />
<br />
&nbsp; &nbsp; NSLog(@&quot;%lu&quot;,[testObject retainCount]); //1<br />
&nbsp; &nbsp; NSLog(@&quot;%lu&quot;,[self retainCount]); //1<br />
<br />
&nbsp; &nbsp; void(^testBlock)(void) = ^{<br />
&nbsp; &nbsp; &nbsp; &nbsp;NSLog(@&quot;The Test String : %@&quot;, testObject);<br />
&nbsp; &nbsp; &nbsp;NSLog(@&quot;The Window Object : %@&quot;, _window);<br />
&nbsp; };<br />
<br />
&nbsp; NSLog(@&quot;%lu&quot;,[testObject retainCount]); //1<br />
&nbsp; &nbsp;NSLog(@&quot;%lu&quot;,[self retainCount]); //1<br />
<br />
&nbsp; &nbsp; void(^testBlock2)(void) = Block_copy(testBlock); //testBlock会被拷贝到heap中,所以用完了要自己调用Block_release进行释放<br />
&nbsp; &nbsp; NSLog(@&quot;%lu&quot;,[testObject retainCount]); //2<br />
&nbsp; &nbsp; NSLog(@&quot;%lu&quot;,[self retainCount]); //2<br />
<br />
&nbsp; &nbsp; testBlock2();<br />
<br />
&nbsp; &nbsp; Block_release(testBlock2);<br />
<br />
&nbsp; &nbsp; NSLog(@&quot;%lu&quot;,[testObject retainCount]); //1<br />
&nbsp; &nbsp; NSLog(@&quot;%lu&quot;,[self retainCount]); //1<br />
<br />
&nbsp; &nbsp; [testObject release];</div></td></tr></tbody></table></div>
</pre>
<p>Block的闭包特性使得Block可以脱离其定义的作用域进行运行，所以你可以在一个函数中返回一个Block，在别的线程或者当前线程的RunLoop中进行运行，而不用担心那些引用到的外部变量是否被释放掉了。</p>
<h3 id="block">4.Block实际应用</h3>
<p>那么我们一般什么时候会用到Block呢？<br />
Blocks通常是一小段自包含的代码片段.所以它经常被用于多线程运行的代码单元(如GCD)，或用于处理聚合类元素单元，或者作为某个函数调用完成后的回调函数.</p>
<p>Block用作回调函数比传统的回调函数有以下的优越性:</p>
<ul>
<li>在函数调用的时候，将Block作为一个参数传给函数</li>
<li>允许访问本地变量，这样可以避免通过结构体将本地变量封装后传递给回调函数</li>
</ul>
<p><strong>应用1: Animations &amp; Completion Handler</strong> </p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp;[UIView animateWithDuration:2 <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; animations:^{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;self.view.backgroundColor = [UIColor redColor];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;completion:^(BOOL finished){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if (finished){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;self.view.backgroundColor = [UIColor blueColor];<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;} <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}];</div></td></tr></tbody></table></div>
</pre>
<p><strong>应用2: Enumeration</strong> <br />
对数据集合类中的每一个元素进行遍历，每次传入一个对象，进行处理</p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">NSArray *cards = [NSArray arrayWithObjects:@&quot;Jack&quot;, @&quot;Queen&quot;, @&quot;King&quot;, @&quot;Ace&quot;, nil];<br />
[cards enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {<br />
&nbsp; &nbsp; NSLog(@&quot;%@ card at index %d&quot;, object, index);<br />
}];</div></td></tr></tbody></table></div>
</pre>
<p><strong>应用3: Notification Handler</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; [[NSNotificationCenter defaultCenter] <br />
&nbsp; &nbsp; &nbsp;addObserverForName:@&quot;TestNotification&quot; <br />
&nbsp; &nbsp; &nbsp;object:nil <br />
&nbsp; &nbsp; &nbsp;queue:aNSOperationQueue <br />
&nbsp; &nbsp; &nbsp;usingBlock:^(NSNotification *notification){<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;NSLog(@&quot;Notification: %@&quot;,notification);<br />
&nbsp; &nbsp; &nbsp;}];</div></td></tr></tbody></table></div>
</pre>
<p><strong>应用4: GCD</strong></p>
<pre>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;dispatch_queue_t imageDownloadQueue = dispatch_queue_create(&quot;Image Download Queue&quot;, NULL);<br />
&nbsp; &nbsp;dispatch_async(imageDownloadQueue, ^{<br />
&nbsp; &nbsp; &nbsp; NSURL *imageURL = [NSURL URLWithString:@&quot;http://xxx.xx.com/a.png&quot;];<br />
&nbsp; &nbsp; &nbsp; NSData *imageData = [NSData dataWithContentsOfURL:imageURL];<br />
&nbsp; &nbsp; &nbsp; UIImage *image = [UIImage imageWithData:imageData];<br />
&nbsp; &nbsp; &nbsp; dispatch_async(dispatch_get_main_queue(), ^{<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [imageView setImage:image];<br />
&nbsp; &nbsp; &nbsp; });<br />
&nbsp; });</div></td></tr></tbody></table></div>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2012/01/block/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NSButton With Hover and Active States</title>
		<link>http://geeklu.com/2011/11/nsbutton-with-hover-and-active-states/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=nsbutton-with-hover-and-active-states</link>
		<comments>http://geeklu.com/2011/11/nsbutton-with-hover-and-active-states/#comments</comments>
		<pubDate>Thu, 24 Nov 2011 03:08:27 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31101</guid>
		<description><![CDATA[如果我想要一个按钮有三种不同的状态:普通状态,鼠标移上去的状态,以及鼠标按下去的状态 首先这是一个功能按钮，按下去会触发相应的动作，比如弹出一个新的窗口。 NSButton 支持设置图片，且可以分别位普通状态和alternate状态(按下去的状态)设置图片，如果你没有设置alternate状态的图片，则按下的时候则显示的比之前的暗些。 12– setImage: – setAlternateImage: 为了实现既定的效果，大体的思路是这样的： 1.当鼠标不在按钮上的时候 按钮设置成普通按钮图片 2.当鼠标移到按钮上的时候 按钮设置成hover状态的图片 3.按钮被按下的时候，就使用默认的变暗效果（在hover的基础上变暗） 所以这个时候关键问题变成如何去”订阅”鼠标移到按钮内部以及移出按钮内部的事件消息! 并在相应的响应函数中作出相应的图片设置显示操作。 Mouse Event NSButton使用的使用的鼠标事件处理方式为 “The Mouse-Tracking Loop Approach” 就是只有NSButton的 1- &#40;void&#41;mouseDown:&#40;NSEvent *&#41;theEvent; 会被调用，所有的鼠标左键事情比如mouseDragged,mouseUp等都会被放到一个队列中，可以通过 1- &#40;NSEvent *&#41;nextEventMatchingMask:&#40;NSUInteger&#41;mask untilDate:&#40;NSDate *&#41;expiration inMode:&#40;NSString *&#41;mode dequeue:&#40;BOOL&#41;flag 指定mask(比如NSLeftMouseUpMask,NSLeftMouseDraggedMask)获得事情队列中的指定事件.然后对事情做相应的处理。 不过貌似对于hover的效果无需考虑mouseUp和mouseDrag的事件. TrackingArea 我们要找的是鼠标移到按钮区域内和移出按钮区域内的事件. 所以找到了“Using Tracking-Area Objects” 这个的方式也蛮简单， 1- &#40;void&#41;updateTrackingAreas; 继承上面的方法，为当前按钮添加一个Tracking-Area,其实就是设置一个控件内的矩形区域以及设置如何跟踪(比如设置跟踪的event NSTrackingMouseEnteredAndExited，NSTrackingMouseMoved，NSTrackingCursorUpdate，设置跟踪的时机 NSTrackingActiveWhenFirstResponder &#8230;.). 设置好了之后相应的函数便可以在相应的事件触发后得到响应了。 实现的关键代码片段，继承NSButton 1234567891011121314151617181920212223242526272829303132- &#40;void&#41;setNormalImage:&#40;NSImage *&#41;image&#123; [...]]]></description>
			<content:encoded><![CDATA[<p>如果我想要一个按钮有三种不同的状态:普通状态,鼠标移上去的状态,以及鼠标按下去的状态<br />
<a href="http://geeklu.com/wp-content/uploads/2011/11/Screen-Shot-2011-11-23-at-11.41.06-PM.png" rel="lightbox[31101]"><img src="http://geeklu.com/wp-content/uploads/2011/11/Screen-Shot-2011-11-23-at-11.41.06-PM.png" alt="" title="Screen Shot 2011-11-23 at 11.41.06 PM" width="29" height="23" class="alignnone size-full wp-image-31104" /></a><a href="http://geeklu.com/wp-content/uploads/2011/11/Screen-Shot-2011-11-23-at-11.41.19-PM.png" rel="lightbox[31101]"><img src="http://geeklu.com/wp-content/uploads/2011/11/Screen-Shot-2011-11-23-at-11.41.19-PM.png" alt="" title="Screen Shot 2011-11-23 at 11.41.19 PM" width="35" height="25" class="alignnone size-full wp-image-31103" /></a><a href="http://geeklu.com/wp-content/uploads/2011/11/Screen-Shot-2011-11-23-at-11.41.47-PM.png" rel="lightbox[31101]"><img src="http://geeklu.com/wp-content/uploads/2011/11/Screen-Shot-2011-11-23-at-11.41.47-PM.png" alt="" title="Screen Shot 2011-11-23 at 11.41.47 PM" width="40" height="27" class="alignnone size-full wp-image-31102" /></a></p>
<p>首先这是一个功能按钮，按下去会触发相应的动作，比如弹出一个新的窗口。<br />
NSButton 支持设置图片，且可以分别位普通状态和alternate状态(按下去的状态)设置图片，如果你没有设置alternate状态的图片，则按下的时候则显示的比之前的暗些。</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">– setImage<span style="color: #002200;">:</span><br />
– setAlternateImage<span style="color: #002200;">:</span></div></td></tr></tbody></table></div>
<p>为了实现既定的效果，大体的思路是这样的：<br />
1.当鼠标不在按钮上的时候 按钮设置成普通按钮图片<br />
2.当鼠标移到按钮上的时候 按钮设置成hover状态的图片<br />
3.按钮被按下的时候，就使用默认的变暗效果（在hover的基础上变暗）</p>
<p>所以这个时候关键问题变成如何去”订阅”鼠标移到按钮内部以及移出按钮内部的事件消息!<br />
并在相应的响应函数中作出相应的图片设置显示操作。</p>
<span id="Mouse_Event"><h3>Mouse Event</h3></span>
<p>NSButton使用的使用的鼠标事件处理方式为 <a href="http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/HandlingMouseEvents.html#//apple_ref/doc/uid/10000060i-CH6-SW4">“The Mouse-Tracking Loop Approach”</a><br />
就是只有NSButton的</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>mouseDown<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/"><span style="color: #400080;">NSEvent</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>theEvent;</div></td></tr></tbody></table></div>
<p>会被调用，所有的鼠标左键事情比如mouseDragged,mouseUp等都会被放到一个队列中，可以通过</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/"><span style="color: #400080;">NSEvent</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>nextEventMatchingMask<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span>NSUInteger<span style="color: #002200;">&#41;</span>mask untilDate<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/"><span style="color: #400080;">NSDate</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>expiration inMode<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/"><span style="color: #400080;">NSString</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>mode dequeue<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">BOOL</span><span style="color: #002200;">&#41;</span>flag</div></td></tr></tbody></table></div>
<p>指定mask(比如NSLeftMouseUpMask,NSLeftMouseDraggedMask)获得事情队列中的指定事件.然后对事情做相应的处理。<br />
不过貌似对于hover的效果无需考虑mouseUp和mouseDrag的事件.</p>
<span id="TrackingArea"><h3>TrackingArea</h3></span>
<p>我们要找的是鼠标移到按钮区域内和移出按钮区域内的事件.<br />
所以找到了<a href="http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/TrackingAreaObjects/TrackingAreaObjects.html#//apple_ref/doc/uid/10000060i-CH8-SW1">“Using Tracking-Area Objects”</a><br />
这个的方式也蛮简单，</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>updateTrackingAreas;</div></td></tr></tbody></table></div>
<p>继承上面的方法，为当前按钮添加一个Tracking-Area,其实就是设置一个控件内的矩形区域以及设置如何跟踪(比如设置跟踪的event NSTrackingMouseEnteredAndExited，NSTrackingMouseMoved，NSTrackingCursorUpdate，设置跟踪的时机 NSTrackingActiveWhenFirstResponder &#8230;.).<br />
设置好了之后相应的函数便可以在相应的事件触发后得到响应了。</p>
<p>实现的关键代码片段，继承NSButton</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:500px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br />30<br />31<br />32<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>setNormalImage<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSImage_Class/"><span style="color: #400080;">NSImage</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>image<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>normalImage<span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>normalImage release<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#125;</span><br />
&nbsp; &nbsp; normalImage <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>image retain<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self setImage<span style="color: #002200;">:</span>normalImage<span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span><br />
<br />
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>updateTrackingAreas<br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>super updateTrackingAreas<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; <span style="color: #a61390;">if</span> <span style="color: #002200;">&#40;</span>trackingArea<span style="color: #002200;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self removeTrackingArea<span style="color: #002200;">:</span>trackingArea<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #002200;">&#91;</span>trackingArea release<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#125;</span><br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; NSTrackingAreaOptions options <span style="color: #002200;">=</span> NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp;<br />
&nbsp; &nbsp; trackingArea <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span><span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTrackingArea_Class/"><span style="color: #400080;">NSTrackingArea</span></a> alloc<span style="color: #002200;">&#93;</span> initWithRect<span style="color: #002200;">:</span>NSZeroRect options<span style="color: #002200;">:</span>options owner<span style="color: #002200;">:</span>self userInfo<span style="color: #002200;">:</span><span style="color: #a61390;">nil</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self addTrackingArea<span style="color: #002200;">:</span>trackingArea<span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span><br />
<br />
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>mouseEntered<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/"><span style="color: #400080;">NSEvent</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>event<br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self setImage<span style="color: #002200;">:</span>hoverImage<span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span><br />
<br />
<span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">void</span><span style="color: #002200;">&#41;</span>mouseExited<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSEvent_Class/"><span style="color: #400080;">NSEvent</span></a> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>event<br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>self setImage<span style="color: #002200;">:</span>normalImage<span style="color: #002200;">&#93;</span>;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2011/11/nsbutton-with-hover-and-active-states/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Make YUNIO Your Git Repo</title>
		<link>http://geeklu.com/2011/11/make-yunio-your-git-repo/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=make-yunio-your-git-repo</link>
		<comments>http://geeklu.com/2011/11/make-yunio-your-git-repo/#comments</comments>
		<pubDate>Wed, 23 Nov 2011 11:12:35 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31100</guid>
		<description><![CDATA[http://www.yun.io/ 一个云存储同步空间，有Mac的客户端，虽然是QT写的，但是功能还OK。 可以将云存储空间挂载到本地的一个目录。 这些云存储空间其实可以用来作为自己的远程Git仓库 12345678#在YUNIO的目录中创建一个bare的git库，作为远程git库 cd /Users/Luke/YUNIO/Projects/ mkdir bookhelper.git cd bookhelper.git/ git init --bare #在原项目的本地git库中添加远程仓库,并提交 git remote add yunio /Users/Luke/YUNIO/Projects/bookhelper.git git push yunio master]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.yun.io/">http://www.yun.io/</a>  一个云存储同步空间，有Mac的客户端，虽然是QT写的，但是功能还OK。<br />
可以将云存储空间挂载到本地的一个目录。<br />
这些云存储空间其实可以用来作为自己的远程Git仓库</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;">#在YUNIO的目录中创建一个bare的git库，作为远程git库</span><br />
<span style="color: #7a0874; font-weight: bold;">cd</span> <span style="color: #000000; font-weight: bold;">/</span>Users<span style="color: #000000; font-weight: bold;">/</span>Luke<span style="color: #000000; font-weight: bold;">/</span>YUNIO<span style="color: #000000; font-weight: bold;">/</span>Projects<span style="color: #000000; font-weight: bold;">/</span><br />
<span style="color: #c20cb9; font-weight: bold;">mkdir</span> bookhelper.git<br />
<span style="color: #7a0874; font-weight: bold;">cd</span> bookhelper.git<span style="color: #000000; font-weight: bold;">/</span><br />
<span style="color: #c20cb9; font-weight: bold;">git init</span> <span style="color: #660033;">--bare</span><br />
<span style="color: #666666; font-style: italic;">#在原项目的本地git库中添加远程仓库,并提交</span><br />
<span style="color: #c20cb9; font-weight: bold;">git remote</span> add yunio <span style="color: #000000; font-weight: bold;">/</span>Users<span style="color: #000000; font-weight: bold;">/</span>Luke<span style="color: #000000; font-weight: bold;">/</span>YUNIO<span style="color: #000000; font-weight: bold;">/</span>Projects<span style="color: #000000; font-weight: bold;">/</span>bookhelper.git<br />
<span style="color: #c20cb9; font-weight: bold;">git push</span> yunio master</div></td></tr></tbody></table></div>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2011/11/make-yunio-your-git-repo/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>iPhone中Png图片格式的研究</title>
		<link>http://geeklu.com/2011/10/iphone-cgbi-png-format/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=iphone-cgbi-png-format</link>
		<comments>http://geeklu.com/2011/10/iphone-cgbi-png-format/#comments</comments>
		<pubDate>Tue, 25 Oct 2011 05:28:57 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31094</guid>
		<description><![CDATA[有时候我们看到一个App，想看看他的一些界面是如何实现的，这个时候需要查看一下它的图片资源，不过iOS的png图片编译后一般的图片阅读器都是没法查看的，本文将告诉的原因和转换出原图的方法（得安装XCode）。 ipa 解压，将png相关文件夹拷贝出来，在命令行下使用/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -revert-iphone-optimizations xxx.png yyy.png 我们都知道一个编译好的iPhone app 其中的png图片一般普通的图片阅读器是无法直接读取的，这是因为XCode在编译的过程中，将图片进行了优化，实际上它已经不是一个png图片了。 这边有一些apple iPhone png自己格式的一些说明 http://iphonedevwiki.net/index.php/CgBI_file_format 在Png数据中，我们最关心的莫过于png的数据块，其中包含了png每一个像素的信息，当然了为了减少存储空间，这些像素信息都是压缩保存的。而且是使用zlib进行压缩的，压缩后 包含zlib header 信息，还有由于解压验证的crc信息。 而iPhone的CgBI格式的png则将原始的png图片作如下变化: 增加一个新的关键块 CgBI Chunk 四个字节 zlib的header和CRC信息全部从IDAT中移除 红蓝交换，每一个像素（RGBA）中的R和B进行调换变成BGRA ，解压后每一个像素有四个字节组成，也就是将每一个像素的 第一个字节和第三个字节调换 透明像素处理 Premultiplied Alpha ，这个的意思是为了图像加载变得更快，预先将Alpha的信息乘到像素的颜色信息中去，这样后期计算的时候就可以减少CPU或者GPU计算了 把一个正常的PNG图片优化成iPhone 的png图片格式可以使用XCode自带的工具 /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -iphone 还有一个第三方的开源工具也可以 https://github.com/DHowett/pincrush 如果你想把一个经过优化后的图片还原成普通图片阅读器可以查看的png图片，就是对上面的过程进行反向处理。 现在可以找到的第三方的转换的一般有如下几个 ipin.py(Python版本) http://www.axelbrz.com.ar/?mod=iphone-png-images-normalizer iPhonePNG（C版本） http://www.newsfirerss.com/blog/?p=176 经过本人测试，上面的这种第三方的额转换工具都没有对alpha相关的做任何处理，也可以是别的原因，有一些图片转换后的结果和原始图片还是有些出入的。 原图(Flip Board中的一个按钮背景图): 编译后如果使用第三方的python或者C版本的代码来转换，转换后的图片都是这样的，感觉边角的像素有点问题，不过大部分情况下 ，图片都是ok的 我尝试通过修改第三方的代码，想将Premultiplied Alpha 还原过去，但是还是存在各种问题，最终没有结果。 只能最终采用XCode自带的工具进行转换 /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -revert-iphone-optimizations 1.png 2.png [...]]]></description>
			<content:encoded><![CDATA[<p>有时候我们看到一个App，想看看他的一些界面是如何实现的，这个时候需要查看一下它的图片资源，不过iOS的png图片编译后一般的图片阅读器都是没法查看的，本文将告诉的原因和转换出原图的方法（得安装XCode）。</p>
<blockquote><p>ipa 解压，将png相关文件夹拷贝出来，在命令行下使用/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -revert-iphone-optimizations xxx.png yyy.png</p></blockquote>
<p>我们都知道一个编译好的iPhone app 其中的png图片一般普通的图片阅读器是无法直接读取的，这是因为XCode在编译的过程中，将图片进行了优化，实际上它已经不是一个png图片了。<br />
这边有一些apple iPhone png自己格式的一些说明<br />
<a href="http://iphonedevwiki.net/index.php/CgBI_file_format">http://iphonedevwiki.net/index.php/CgBI_file_format</a></p>
<p>在Png数据中，我们最关心的莫过于png的数据块，其中包含了png每一个像素的信息，当然了为了减少存储空间，这些像素信息都是压缩保存的。而且是使用zlib进行压缩的，压缩后 包含zlib header 信息，还有由于解压验证的crc信息。<br />
而iPhone的CgBI格式的png则将原始的png图片作如下变化:</p>
<blockquote><ol>
<li>增加一个新的关键块 CgBI Chunk 四个字节</li>
<li>zlib的header和CRC信息全部从IDAT中移除</li>
<li>红蓝交换，每一个像素（RGBA）中的R和B进行调换变成BGRA ，解压后每一个像素有四个字节组成，也就是将每一个像素的 第一个字节和第三个字节调换</li>
<li>透明像素处理 Premultiplied Alpha ，这个的意思是为了图像加载变得更快，预先将Alpha的信息乘到像素的颜色信息中去，这样后期计算的时候就可以减少CPU或者GPU计算了</li>
</ol>
</blockquote>
<p>把一个正常的PNG图片优化成iPhone 的png图片格式可以使用XCode自带的工具 /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -iphone<br />
还有一个第三方的开源工具也可以<br />
<a href="https://github.com/DHowett/pincrush">https://github.com/DHowett/pincrush</a></p>
<p>如果你想把一个经过优化后的图片还原成普通图片阅读器可以查看的png图片，就是对上面的过程进行反向处理。<br />
现在可以找到的第三方的转换的一般有如下几个<br />
ipin.py(Python版本) <a href="http://www.axelbrz.com.ar/?mod=iphone-png-images-normalizer">http://www.axelbrz.com.ar/?mod=iphone-png-images-normalizer</a><br />
iPhonePNG（C版本） <a href="http://www.newsfirerss.com/blog/?p=176">http://www.newsfirerss.com/blog/?p=176</a></p>
<p>经过本人测试，上面的这种第三方的额转换工具都没有对alpha相关的做任何处理，也可以是别的原因，有一些图片转换后的结果和原始图片还是有些出入的。<br />
原图(Flip Board中的一个按钮背景图):<br />
<img src="http://geeklu.com/wp-content/uploads/2011/10/paperbutton-back111.png" alt="" title="paperbutton-back111" width="19" height="42" class="alignnone size-full wp-image-31098" /><br />
编译后如果使用第三方的python或者C版本的代码来转换，转换后的图片都是这样的，感觉边角的像素有点问题，不过大部分情况下 ，图片都是ok的<br />
<img src="http://geeklu.com/wp-content/uploads/2011/10/paperbutton-back-21-57-54-512.png" alt="" title="paperbutton-back 21-57-54-512" width="19" height="42" class="alignnone size-full wp-image-31097" /></p>
<p>我尝试通过修改第三方的代码，想将Premultiplied Alpha 还原过去，但是还是存在各种问题，最终没有结果。<br />
只能最终采用XCode自带的工具进行转换 /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/pngcrush -revert-iphone-optimizations 1.png 2.png<br />
这个<a href="http://pmt.sourceforge.net/pngcrush/">pngcrush</a>是apple改自开源的pngcrush 只可惜苹果修改后的版本却没有开源出来。<br />
<br />
为了避免每次都需要在命令行中进行操作，你可以通过automator新建一个shell的service<br />
<img class="alignnone size-full wp-image-31095" title="Screen Shot 2011-10-25 at 1.16.29 PM" src="http://geeklu.com/wp-content/uploads/2011/10/Screen-Shot-2011-10-25-at-1.16.29-PM.png" alt="" width="569" height="506" /></p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br /></div></td><td><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">for</span> path <span style="color: #000000; font-weight: bold;">in</span> <span style="color: #ff0000;">&quot;$@&quot;</span><br />
<span style="color: #000000; font-weight: bold;">do</span><br />
<span style="color: #c20cb9; font-weight: bold;">mv</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$path</span>&quot;</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$path</span>&quot;</span>.tmp<br />
<span style="color: #000000; font-weight: bold;">/</span>Developer<span style="color: #000000; font-weight: bold;">/</span>Platforms<span style="color: #000000; font-weight: bold;">/</span>iPhoneOS.platform<span style="color: #000000; font-weight: bold;">/</span>Developer<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>pngcrush <span style="color: #660033;">-revert-iphone-optimizations</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$path</span>&quot;</span>.tmp <span style="color: #ff0000;">&quot;<span style="color: #007800;">$path</span>&quot;</span><br />
<span style="color: #c20cb9; font-weight: bold;">rm</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$path</span>&quot;</span>.tmp<br />
<span style="color: #000000; font-weight: bold;">done</span></div></td></tr></tbody></table></div>
<p>当然你可以修改脚本，并可以作用于文件和文件夹，对目标进行判断，文件夹则递归文件夹中的png文件进行逐个处理。</p>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2011/10/iphone-cgbi-png-format/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SWATCH 计时器的使用</title>
		<link>http://geeklu.com/2011/10/swatch-timer/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=swatch-timer</link>
		<comments>http://geeklu.com/2011/10/swatch-timer/#comments</comments>
		<pubDate>Mon, 17 Oct 2011 15:31:45 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31091</guid>
		<description><![CDATA[一般Swatch手表的计时都是采用所谓的三针计时方式：一个是秒针，一个是分针，另外一个是1/10秒针，当开始计时时，秒针开始计时，走到一圈时即是一分，分针就计为1分，当终止计时时，1/10秒针计算的是1/10秒的时间。所以三针计时方式可以精确到1/10秒。 今天刚拿到上图中的这块表，完全搞不定如何计时。最大的误解就在于那个最长的秒针。尼玛这个最长的秒针不是手表本身的秒针，而是计时的秒针的啊，这完全违反一般的认识啊!!!! 而下方的小圆盘中的不停走动的秒针才是表的正真的秒针啊!!! 左上方是用于计分钟的，一格是一分，右上方是记1/10秒的，一格是1/10秒。而中间的这个最大的秒针是计时用的秒针。 表的右侧有三个功能按钮，在计时归零的状态下，按一下最上方的按钮，最大的秒针开始走动，代表开始计时，再按一下暂停计时，这个时候，1/10秒的地方就会显示对应的10分之几秒，然后再按上方的按钮则继续计时，再按暂停计时。在暂停的时候按下下方的按钮，则进行计时归零。如果归零有偏位，则可以通过拔开中间的按钮一档，然后按上方或者下方的按钮进行秒针和1/10秒的归零位置调整。 说实话，swatch计时功能使用最大的障碍就是使用最大的秒针来作为计时用的秒针（而一般人的认识中这个大指针是表的秒针）。]]></description>
			<content:encoded><![CDATA[<p><img src="http://geeklu.com/wp-content/uploads/2011/10/A48-798-457.jpg" alt="" title="A48-798-457" width="380" height="285" class="alignnone size-full wp-image-31092" /></p>
<p>一般Swatch手表的计时都是采用所谓的三针计时方式：一个是秒针，一个是分针，另外一个是1/10秒针，当开始计时时，秒针开始计时，走到一圈时即是一分，分针就计为1分，当终止计时时，1/10秒针计算的是1/10秒的时间。所以三针计时方式可以精确到1/10秒。</p>
<p>今天刚拿到上图中的这块表，完全搞不定如何计时。最大的误解就在于那个最长的秒针。尼玛这个最长的秒针不是手表本身的秒针，而是计时的秒针的啊，这完全违反一般的认识啊!!!! 而下方的小圆盘中的不停走动的秒针才是表的正真的秒针啊!!!</p>
<p>左上方是用于计分钟的，一格是一分，右上方是记1/10秒的，一格是1/10秒。而中间的这个最大的秒针是计时用的秒针。<br />
表的右侧有三个功能按钮，在计时归零的状态下，按一下最上方的按钮，最大的秒针开始走动，代表开始计时，再按一下暂停计时，这个时候，1/10秒的地方就会显示对应的10分之几秒，然后再按上方的按钮则继续计时，再按暂停计时。在暂停的时候按下下方的按钮，则进行计时归零。如果归零有偏位，则可以通过拔开中间的按钮一档，然后按上方或者下方的按钮进行秒针和1/10秒的归零位置调整。</p>
<p>说实话，swatch计时功能使用最大的障碍就是使用最大的秒针来作为计时用的秒针（而一般人的认识中这个大指针是表的秒针）。</p>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2011/10/swatch-timer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>浅谈Runloop</title>
		<link>http://geeklu.com/2011/08/cocoa-runloop/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cocoa-runloop</link>
		<comments>http://geeklu.com/2011/08/cocoa-runloop/#comments</comments>
		<pubDate>Sat, 13 Aug 2011 06:20:54 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31089</guid>
		<description><![CDATA[还记得以前用C写p2p聊天程序的时候，都会写一个循环，不断的去侦听某个端口是否有消息，如果有则接受处理，或者监测着缓冲区是否有数据，如果有则发送出去。 也就是说，这种程序不是运行一次就可以关闭了，而是需要一直运行下去的。这样的程序有web容器，桌面程序等待。 其实Runloop就是这种循环的抽象，用来管理代码的执行。在线程的Runloop中，只有执行完当前的任务后，才可以等待执行下一个任务。 把一个公司比作一个运行着的程序(进程)，每一个人都是一个线程，你就在循环的运行着，每天处理着事件。 需要处理的任务数较多，就会进去你的任务队列，一个个的按次序做完，有时候你做完了就空闲了，处于等待状态。 1.你的老板会指派一些任务给你做，这个便是线程之间的消息传递。 2.有一些任务可能是定时的，每隔一定的时间你就需要做一下，当然有时候可能某一个定时任务的时间点到了，但是你的手头上正在处理另一个事情，这个时候可能需要等这个事情处理完了在处理定时任务了。所以在Cocoa中Timer的定时执行并不能保证实时。]]></description>
			<content:encoded><![CDATA[<p>还记得以前用C写p2p聊天程序的时候，都会写一个循环，不断的去侦听某个端口是否有消息，如果有则接受处理，或者监测着缓冲区是否有数据，如果有则发送出去。 也就是说，这种程序不是运行一次就可以关闭了，而是需要一直运行下去的。这样的程序有web容器，桌面程序等待。 其实Runloop就是这种循环的抽象，用来管理代码的执行。在线程的Runloop中，只有执行完当前的任务后，才可以等待执行下一个任务。</p>
<p>  把一个公司比作一个运行着的程序(进程)，每一个人都是一个线程，你就在循环的运行着，每天处理着事件。<br />
需要处理的任务数较多，就会进去你的任务队列，一个个的按次序做完，有时候你做完了就空闲了，处于等待状态。<br />
1.你的老板会指派一些任务给你做，这个便是线程之间的消息传递。<br />
2.有一些任务可能是定时的，每隔一定的时间你就需要做一下，当然有时候可能某一个定时任务的时间点到了，但是你的手头上正在处理另一个事情，这个时候可能需要等这个事情处理完了在处理定时任务了。所以在Cocoa中Timer的定时执行并不能保证实时。</p>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2011/08/cocoa-runloop/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>NSCell copyWithZone</title>
		<link>http://geeklu.com/2011/08/nscell-copywithzone/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=nscell-copywithzone</link>
		<comments>http://geeklu.com/2011/08/nscell-copywithzone/#comments</comments>
		<pubDate>Tue, 09 Aug 2011 04:42:15 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31088</guid>
		<description><![CDATA[有时候，我们的TableView需要显示的内容是自定义样式的。 这个时候你需要创建一个自定义的Cell来渲染你自己想要的内容。 所以你继承了NSCell，并拥有一个数据对象，用来作为渲染的数据来源。 由于NSTableView需要使用到NSCell的copyWithZone方法在适当的时候复制NSCell，而NSCell默认的copyWithZone方法的实现方式是使用NSCopyObject创建了原始cell对象的一份浅拷贝，所以在复制的时候只会简单的复制指针的值，而不会去深拷贝或者去retain，那么这样程序运行的时候就会出错了。原因就是Cell所引用的某个对象被错误的完全释放了（复制的时候没有retain，但是在释放cell的时候，将这个对象release了一把）。所以在实现copyWithZone的时候可以采用如下方法。 12345678910- &#40;id&#41;copyWithZone:&#40;NSZone *&#41;zone &#123; &#160; &#160; id cellCopy = &#91;super copyWithZone:zone&#93;; &#160; &#160; /* Assume that other initialization takes place here. */ &#160; &#160; &#160; cellCopy-&#62;titleCell = nil; &#160; &#160; &#91;cellCopy setTitleCell:&#91;self titleCell&#93;&#93;; &#160; &#160; &#160; return cellCopy; &#125; 参考文档： 1.内存管理编程指南：实现对象复制 2.NSTableView copies cells &#8211; bug or feature?]]></description>
			<content:encoded><![CDATA[<p>有时候，我们的TableView需要显示的内容是自定义样式的。<br />
这个时候你需要创建一个自定义的Cell来渲染你自己想要的内容。<br />
所以你继承了NSCell，并拥有一个数据对象，用来作为渲染的数据来源。</p>
<p>由于NSTableView需要使用到NSCell的copyWithZone方法在适当的时候复制NSCell，而NSCell默认的copyWithZone方法的实现方式是使用NSCopyObject创建了原始cell对象的一份浅拷贝，所以在复制的时候只会简单的复制指针的值，而不会去深拷贝或者去retain，那么这样程序运行的时候就会出错了。原因就是Cell所引用的某个对象被错误的完全释放了（复制的时候没有retain，但是在释放cell的时候，将这个对象release了一把）。所以在实现copyWithZone的时候可以采用如下方法。</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #002200;">-</span> <span style="color: #002200;">&#40;</span><span style="color: #a61390;">id</span><span style="color: #002200;">&#41;</span>copyWithZone<span style="color: #002200;">:</span><span style="color: #002200;">&#40;</span><span style="color: #a61390;">NSZone</span> <span style="color: #002200;">*</span><span style="color: #002200;">&#41;</span>zone<br />
<span style="color: #002200;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #a61390;">id</span> cellCopy <span style="color: #002200;">=</span> <span style="color: #002200;">&#91;</span>super copyWithZone<span style="color: #002200;">:</span>zone<span style="color: #002200;">&#93;</span>;<br />
&nbsp; &nbsp; <span style="color: #11740a; font-style: italic;">/* Assume that other initialization takes place here. */</span><br />
&nbsp;<br />
&nbsp; &nbsp; cellCopy<span style="color: #002200;">-</span>&gt;titleCell <span style="color: #002200;">=</span> <span style="color: #a61390;">nil</span>;<br />
&nbsp; &nbsp; <span style="color: #002200;">&#91;</span>cellCopy setTitleCell<span style="color: #002200;">:</span><span style="color: #002200;">&#91;</span>self titleCell<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#93;</span>;<br />
&nbsp;<br />
&nbsp; &nbsp; <span style="color: #a61390;">return</span> cellCopy;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p>参考文档：<br />
1.<a href="http://www.apple.com.cn/developer/iphone/library/documentation/UserExperience/Conceptual/MemoryMgmt/Articles/mmImplementCopy.html">内存管理编程指南：实现对象复制</a><br />
2.<a href="http://www.cocoabuilder.com/archive/cocoa/57340-nstableview-copies-cells-bug-or-feature.html">NSTableView copies cells &#8211; bug or feature?</a></p>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2011/08/nscell-copywithzone/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mac 多屏显示  编程</title>
		<link>http://geeklu.com/2011/08/mac-multi-display-screen-crop/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=mac-multi-display-screen-crop</link>
		<comments>http://geeklu.com/2011/08/mac-multi-display-screen-crop/#comments</comments>
		<pubDate>Fri, 05 Aug 2011 03:57:34 +0000</pubDate>
		<dc:creator>卢克</dc:creator>
				<category><![CDATA[Mac]]></category>
		<category><![CDATA[Objc]]></category>

		<guid isPermaLink="false">http://geeklu.com/?p=31081</guid>
		<description><![CDATA[一.多屏介绍 mac可以支持多屏显示，有一个屏幕是作为主屏存在的，也就是那个显示Menu Bar以及Dock的屏幕。别的屏幕可以作为扩展屏，或者作为镜像屏幕(其实就是两个屏幕的内容一样)。 我们可以将Menubar 拖到任意一个屏幕（改变主屏），我们可以调整屏幕的左右或者上下位置，我们可以调整屏幕的错位大小（就是上下或者左右拖动屏幕，最大程度的时候，两个屏幕角对角，以保证俩个屏幕有相交区域，鼠标从一个屏幕进入另一个屏幕，需要从从相交的区域进去）。 二.NSScreen类 在Cocoa中，我们可以通过NSScreen类获取所有screen的信息。 可以通过 + (NSArray *)screens 这个类方法获取所有的screen对象，其中第一个元素是放置Menu Bar的屏幕。“The screen containing the menu bar is always the first object (index 0) in the array returned by the screens method.” + (NSScreen *)mainScreen 这个类方法所获得的screen并不一定是包含Menu bar的screen，而是key window（当前响应用户键盘事件的窗口）所在的screen。 我们可以通过- (NSRect)frame方法获得screen的frame，包括menu bar以及dock的区域。 - (NSRect)visibleFrame获得的区域不包含menu bar以及dock占用的区域。 三.计算区域并截图 多个屏幕的情况下，其实系统将所有的screen放置在一个包括所有屏幕的矩形内。 我们可以通过如下方法计算出整个矩形的frame 1234NSRect fullFrame = NSZeroRect; for &#40;NSScreen *screen [...]]]></description>
			<content:encoded><![CDATA[<span id="."><h4>一.多屏介绍</h4></span>
<p>mac可以支持多屏显示，有一个屏幕是作为主屏存在的，也就是那个显示Menu Bar以及Dock的屏幕。别的屏幕可以作为扩展屏，或者作为镜像屏幕(其实就是两个屏幕的内容一样)。<br />
<img class="alignnone size-full wp-image-31082" title="Voila_Capture12" src="http://geeklu.com/wp-content/uploads/2011/08/Voila_Capture12.png" alt="" width="334" height="250" /><br />
我们可以将Menubar 拖到任意一个屏幕（改变主屏），我们可以调整屏幕的左右或者上下位置，我们可以调整屏幕的错位大小（就是上下或者左右拖动屏幕，最大程度的时候，两个屏幕角对角，以保证俩个屏幕有相交区域，鼠标从一个屏幕进入另一个屏幕，需要从从相交的区域进去）。</p>
<span id="NSScreen"><h4>二.NSScreen类</h4></span>
<p>在Cocoa中，我们可以通过NSScreen类获取所有screen的信息。<br />
可以通过 + (NSArray *)screens 这个类方法获取所有的screen对象，其中第一个元素是放置Menu Bar的屏幕。“The screen containing the menu bar is always the first object (index 0) in the array returned by the screens method.”</p>
<p>+ (NSScreen *)mainScreen 这个类方法所获得的screen并不一定是包含Menu bar的screen，而是key window（当前响应用户键盘事件的窗口）所在的screen。</p>
<p>我们可以通过- (NSRect)frame方法获得screen的frame，包括menu bar以及dock的区域。<br />
- (NSRect)visibleFrame获得的区域不包含menu bar以及dock占用的区域。</p>
<span id="._1"><h4>三.计算区域并截图</h4></span>
<p>多个屏幕的情况下，其实系统将所有的screen放置在一个包括所有屏幕的矩形内。<br />
我们可以通过如下方法计算出整个矩形的frame</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #a61390;">NSRect</span> fullFrame <span style="color: #002200;">=</span> NSZeroRect;<br />
<span style="color: #a61390;">for</span> <span style="color: #002200;">&#40;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSScreen_Class/"><span style="color: #400080;">NSScreen</span></a> <span style="color: #002200;">*</span>screen <span style="color: #a61390;">in</span> <span style="color: #002200;">&#91;</span><a href="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSScreen_Class/"><span style="color: #400080;">NSScreen</span></a> screens<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span> <span style="color: #002200;">&#123;</span><br />
fullFrame <span style="color: #002200;">=</span> NSUnionRect<span style="color: #002200;">&#40;</span>fullFrame, <span style="color: #002200;">&#91;</span>screen frame<span style="color: #002200;">&#93;</span><span style="color: #002200;">&#41;</span>;<br />
<span style="color: #002200;">&#125;</span></div></td></tr></tbody></table></div>
<p><img class="alignnone size-full wp-image-31087" title="IMG_0100" src="http://geeklu.com/wp-content/uploads/2011/08/IMG_0100.jpg" alt="" width="600" height="448" /><br />
图中坐标显的是对称的，如果你将其中一个screen上下拖动，就会变得不对称，各个screen的坐标也会随着变化。</p>
<p>在Cocoa中可以通过一个非常简单的方式对屏幕进行截图，方法如下，cropRect为整个screen中的frame。</p>
<div class="codecolorer-container objc dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="objc codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">CGImageRef cgImageRef <span style="color: #002200;">=</span> CGWindowListCreateImage<span style="color: #002200;">&#40;</span>cropRect,<br />
kCGWindowListOptionOnScreenOnly,<br />
kCGNullWindowID,<br />
kCGWindowImageDefault<span style="color: #002200;">&#41;</span>;、</div></td></tr></tbody></table></div>
<p>之前我们用screen的frame获得的区域都是以包含menu bar的屏幕的左下角为坐标原点的。<br />
而在CG的相关方法中，则以左上角为坐标原点。<br />
如果只是在单屏幕的情况下，cropRect的计算就非常简单。但是在多屏的情况下，且各个屏幕的分辨率可能不一样，所以就有点复杂了，比如像刚才的情况，想截图副屏左上角的四分之一的区域，这个时候就需要将包含menubar的screen的左上角作为原点在推算出目标区域坐标。<br />
<img class="alignnone size-full wp-image-31086" title="IMG_0102" src="http://geeklu.com/wp-content/uploads/2011/08/IMG_0102.jpg" alt="" width="600" height="449" /></p>
]]></content:encoded>
			<wfw:commentRss>http://geeklu.com/2011/08/mac-multi-display-screen-crop/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

