<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="http://www.yifeiyang.net/styles/rss.css" type="text/css"?>

<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>YIFEIYANG</title>
	<atom:link href="http://www.yifeiyang.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.yifeiyang.net</link>
	<description>易飞扬的博客</description>
	<lastBuildDate>Sun, 19 Feb 2012 10:48:41 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<image>
<link>http://www.yifeiyang.net</link>
<url>http://www.yifeiyang.net/wp/wp-content/plugins/maxblogpress-favicon/icons/CARTOON5.JPG</url>
<title>YIFEIYANG</title>
</image>
		<item>
		<title>iPhone开发之深入浅出 (3) &#8212; ARC之前世今生</title>
		<link>http://www.yifeiyang.net/development-of-the-iphone-simply-3/</link>
		<comments>http://www.yifeiyang.net/development-of-the-iphone-simply-3/#comments</comments>
		<pubDate>Sun, 19 Feb 2012 10:45:41 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[ARC]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[深入浅出]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/?p=450</guid>
		<description><![CDATA[<div class="contents">
<dt>
<a href="#sec1">Objective-C 内存管理</a>
</dt>
<dd>
<dl>
<dt>
<a href="#sec2">基本原则</a>
</dt>
<dt>
<a href="#sec3">编程准则</a>
</dt>
</dl>
</dd>
<dt>
<a href="#sec4">ARC 诞生</a>
</dt>
<dd>
<dl>
<dt>
<a href="#sec5">__strong</a>
</dt>
<dt>
<a href="#sec6">__weak</a>
</dt>
<dt>
<a href="#sec7">__autoreleasing</a>
</dt>
<dt>
<a href="#sec8">ARC 规则</a>
</dt>
</dl>
</dd>
</div>
<p style="text-align: center;">
<strong>iPhone开发之深入浅出 (3) &#8212; ARC之前世今生</strong>
</p>
<p>前两节我们对 ARC(Aut[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/development-of-the-iphone-simply-3/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt>
<a href="#sec1">Objective-C 内存管理</a>
</dt>
<dd>
<dl>
<dt>
<a href="#sec2">基本原则</a>
</dt>
<dt>
<a href="#sec3">编程准则</a>
</dt>
</dl>
</dd>
<dt>
<a href="#sec4">ARC 诞生</a>
</dt>
<dd>
<dl>
<dt>
<a href="#sec5">__strong</a>
</dt>
<dt>
<a href="#sec6">__weak</a>
</dt>
<dt>
<a href="#sec7">__autoreleasing</a>
</dt>
<dt>
<a href="#sec8">ARC 规则</a>
</dt>
</dl>
</dd>
</div>
<p style="text-align: center;">
<strong>iPhone开发之深入浅出 (3) &mdash; ARC之前世今生</strong>
</p>
<p>前两节我们对 ARC(Automatic Reference Counting) 有了一个基本的理解，但是 ARC 是怎么产生的，为什么苹果要在其最新的 iOS/Mac OS X 上导入该框架？ 如果不理解其背后的基本原理，只是死记硬背那些规则/方法，是毫无意义的。就像我们从小接受的填鸭式教育，基本上到后来都还给老师了。</p>
<p>本节，我们先来看看 ARC 产生之前的 Objective-C 内存管理世界，然后再来看看导入 ARC 后，新的 LLVM 编译器在背后为我们做了什么。</p>
<h3><a name="sec1" id="sec1"></a>Objective-C 内存管理</h3>
<p class="first">和许多面向对象语言一样，Objective-C 中内存管理的方式其实就是指 <strong>引用计数</strong> （Reference Counting）的使用准则。如下图所示，对象生成的时候必定被某个持有者拿着，如果有多个持有者的话，其引用计数就会递增；相反失去一个持有者那么引用计数即会递减，直到失去所有的持有者，才真正地从内测中释放自己。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/ARC/ARC1.png" rel="lightbox[450]"><br />
<img src="http://www.yifeiyang.net/images/iphone/ARC/ARC1.png" alt="ARC"/></a>
</p>
</div>
<h4><a name="sec2" id="sec2"></a>基本原则</h4>
<dl>
<dt><strong>内存管理的依循下面的基本原则</strong></dt>
<dd></dd>
</dl>
<ul>
<li>自己生成的对象，那么既是其持有者</li>
<li>不是自己生成的对象，也可成为其持有者（一个对象可以被多个人持有）</li>
<li>如果不想持有对象的时候，必须释放其所有权</li>
<li>不能释放已不再持有所有权的对象</li>
</ul>
<p>结合 Objective-C 语言中的方法，我们来看看基本的内存管理。</p>
<table class="muse-table" border="2" cellpadding="5">
<thead>
<tr>
<th>方法</th>
<th>动作</th>
</tr>
</thead>
<tbody>
<tr>
<td>alloc/new/copy/mutableCopy</td>
<td>生成对象并拥有所有权</td>
</tr>
<tr>
<td>retain</td>
<td>拥有对象所有权</td>
</tr>
<tr>
<td>release</td>
<td>释放对象所有权</td>
</tr>
<tr>
<td>dealloc</td>
<td>释放对象资源</td>
</tr>
</tbody>
</table>
<p>实际上这些函数并不能说是 Objective-C 语言所特有的，而是 OS X / iOS 系统库中包含的基类函数；具体说就是 Cocoa Framework::Foundation::NSObject 基类的成员函数。</p>
<p>Objective-C 语言内部严格遵守上面表格中的定义；首先是 alloc/new/copy/mutableCopy 这几个函数，并且是alloc/new/copy/mutableCopy 开头的函数，比如：allpcMyObject/newTheObject/copyThis/mutableCopyTheObject 等都必须遵循这个原则。</p>
<p>反而言之，如果不是 alloc/new/copy/mutableCopy 开头的函数，而且要返回对象的话，那么调用端只是生成对象，而不是其持有者。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">-(<span class="type">id</span>)<span class="function-name">allocObject</span> {
    <span class="comment-delimiter">/*</span><span class="comment">
     * &#29983;&#25104;&#23545;&#35937;&#24182;&#25317;&#26377;&#25152;&#26377;&#26435;
     */</span>
    <span class="type">id</span> <span class="variable-name">obj</span> = [[<span class="type">NSObject</span> alloc] init];

    <span class="comment-delimiter">/*</span><span class="comment">
     * &#33258;&#24049;&#19968;&#30452;&#26159;&#25345;&#26377;&#23545;&#35937;&#29366;&#24577;
     */</span>
    <span class="keyword">return</span> obj;
}
</pre>
</td>
<tr></table>
</div>
<p>如上面的例子，alloc 生成的对象，其所有权会传递给函数的调用端；即满足了 alloc 开头函数的命名规则。</p>
<dl>
<dt><strong>再看下面的例子</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">-(<span class="type">id</span>)<span class="function-name">object</span> {
    <span class="type">id</span> <span class="variable-name">obj</span> = [[<span class="type">NSObject</span> alloc] init];

    <span class="comment-delimiter">/*</span><span class="comment">
     * &#33258;&#24049;&#19968;&#30452;&#26159;&#25345;&#26377;&#23545;&#35937;&#29366;&#24577;
     */</span>

    [obj autorelease];

    <span class="comment-delimiter">/*</span><span class="comment">
     * &#23545;&#35937;&#36824;&#23384;&#22312;&#65292;&#21482;&#26159;&#24182;&#19981;&#25345;&#26377;&#23427;&#30340;&#25152;&#26377;&#26435;
     */</span>

    <span class="keyword">return</span> obj;
}
</pre>
</td>
<tr></table>
</div>
<p>这里我们用到了 autorelease 函数。它的作用既是将对象放入 NSAutoreleasePool 中，由其来维护其生命周期。换句话说对象的持有者是 NSAutoreleasePool；上面的例子中，object 返回后，调用者将不持有其所有权。（除非再调用 retain。）</p>
<p>用 autorelease 的一个理由既是让程序员来控制对象的存活周期，而不像 C/C++ 等语言中，出栈后，栈中数据都被自动废弃，或者用 <strong>{ }</strong>  框住的自动变量，当出了范围就看不到了。在 Objective-C 中，只有当 [pool drain] 被调用的时候，才清空 pool 中所有登录的对象实体，在这之前，你可以像往常一样正常使用对象。</p>
<p>当然可以想象得到的，如果一个程序只有一个 NSAutoreleasePool，并在 main 中声明，程序结束时才 [pool drain]/[pool release] 的话，那么所有 autorelease 的对象都将塞满这个 pool，会耗掉系统大部分内存。所以，使用 NSAutoreleasePool 的时候也尽量建议局部使用，比如下面的循环。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="keyword">for</span> (i=0; i &lt; 100; i++) {
    <span class="type">NSAutoreleasePool</span>* <span class="variable-name">pool</span> = [[<span class="type">NSAutoreleasePool</span> alloc] init];

    <span class="comment-delimiter">// </span><span class="comment">&#19979;&#38754;&#30340;&#20989;&#25968;&#30001;&#20110;&#19981;&#23646;&#20110; alloc/new/copy/mutableCopy &#33539;&#30068;&#30340;&#20989;&#25968;&#65292;&#25152;&#20197;&#37117;&#20351;&#29992;&#20102; autorelease
</span>    <span class="type">NSMutableArray</span>* <span class="variable-name">array</span> = [<span class="type">NSMutableArray</span> array];
    <span class="type">NSString</span> *<span class="variable-name">str</span> = [<span class="type">NSString</span> stringWithFormat:@<span class="string">"TestCode"</span>];

    <span class="comment-delimiter">/*</span><span class="comment">
     * &#20854;&#20182;&#20351;&#29992;autorelease&#23450;&#20041;&#30340;&#23545;&#35937;
     */</span>
    <span class="type">Test</span> *<span class="variable-name">test</span> = [[[<span class="type">Test</span> alloc] init] autorelease];

    <span class="comment-delimiter">// </span><span class="comment">&#36890;&#36807;&#19979;&#38754;&#30340;&#20989;&#25968;&#65292;&#21487;&#20197;&#38543;&#26102;&#30417;&#25511;pool&#20013;&#30340;&#23545;&#35937;
</span>    <span class="comment-delimiter">// </span><span class="comment">iOS&#20197;&#22806;&#30340;&#36816;&#34892;&#24211;&#30340;&#24773;&#20917;&#19979;&#65292;&#20063;&#21487;&#20197;&#20351;&#29992; _objc_autoreleasePoolPrint() &#31169;&#26377;&#20989;&#25968;&#65292;&#21482;&#26159;&#38656;&#35201;&#19979;&#38754;&#30340;&#22768;&#26126;
</span>    <span class="comment-delimiter">// </span><span class="comment">extern void _objc_autoreleasePoolPrint();
</span>    [<span class="type">NSAutoreleasePool</span> showPools];

    <span class="comment-delimiter">// </span><span class="comment">&#36825;&#37324;&#25226;&#25152;&#26377;pool&#20013;&#30340;&#23545;&#35937;&#37117;&#37322;&#25918;&#25481;
</span>    [pool release];
}
</pre>
</td>
<tr></table>
</div>
<p>当然 NSAutoreleasePool 也可以嵌套，基本上都依存大括号规则。</p>
<h4><a name="sec3" id="sec3"></a>编程准则</h4>
<p class="first">基于以上原则，在 ARC 诞生之前，我们往往用下面准则来写代码。</p>
<h5>生成对象时，使用autorelease</h5>
<p>一般情况下，我们这样生成对象并使用</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="type">MyController</span>* <span class="variable-name">controller</span> = [[<span class="type">MyController</span> alloc] init];
<span class="comment-delimiter">// </span><span class="comment">......
</span>[controller release];
</pre>
</td>
<tr></table>
</div>
<p>如果在 [controller release] 之前函数return了怎么样，内存泄露了不是；为了防患于未然，一般像下面一样 <strong>生成对象时，使用autorelease</strong>。这样一来，该对象就被自动加入到最近的那个 pool 中。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="type">MyController</span>* <span class="variable-name">controller</span> = [[[<span class="type">MyController</span> alloc] init] autorelease];
</pre>
</td>
<tr></table>
</div>
<h5>对象代入时，先autorelease后再retain</h5>
<p>对象代入的时候，如果之前不将变量所持有的对象释放，那么很可能引起内存泄露。比如下面的代码</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">{
  _member = [[<span class="type">TempValue</span> alloc] init];
}

- (<span class="type">void</span>)<span class="function-name">setValue</span>:(<span class="type">TempValue</span> *)<span class="variable-name">value</span> {
  _member = value;
  <span class="comment-delimiter">// </span><span class="comment">&#36825;&#26102;&#65292;&#20043;&#21069;&#25345;&#26377;&#30340;&#23545;&#35937;&#22240;&#20026;&#27809;&#26377; release &#32780;&#24341;&#36215;&#20869;&#23384;&#27844;&#38706;
</span>  <span class="comment-delimiter">// </span><span class="comment">&#24403;&#28982;&#65292;&#20808; [_member release] &#21518;&#20877;&#20195;&#20837;&#20063;&#26159;&#21487;&#20197;&#30340;&#65292;
</span>  <span class="comment-delimiter">// </span><span class="comment">&#20294;&#26159;&#24403;&#19982;&#12300;&#23545;&#35937;&#22312;&#20989;&#25968;&#20013;&#36820;&#22238;&#26102;&#12301;&#30340;&#38382;&#39064;&#19968;&#21516;&#32771;&#34385;&#26102;&#65292;
</span>  <span class="comment-delimiter">// </span><span class="comment">&#22914;&#26524;&#27809;&#26377; return [[object retain] autorelease] &#30340;&#20445;&#35777;&#65292;&#36825;&#37324;&#21363;&#20351; [_member release]&#20063;&#26159;&#30334;&#25645;
</span>  <span class="comment-delimiter">// </span><span class="comment">&#35814;&#32454;&#30340;&#35299;&#37322;&#35265;&#19979;
</span>}
</pre>
</td>
<tr></table>
</div>
<p>鉴于以上原因，我们将原先的对象先autorelease后再将新对象retain代入。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">{
  _member = [[<span class="type">TempValue</span> alloc] init];
  <span class="comment-delimiter">// </span><span class="comment">&#36825;&#37324;&#65292;&#21363;&#20351;&#20351;&#29992;&#12304;&#29983;&#25104;&#23545;&#35937;&#26102;&#65292;&#20351;&#29992;autorelease&#12305;&#30340;&#20934;&#21017;&#65292;&#20063;&#27809;&#26377;&#20851;&#31995;
</span>  <span class="comment-delimiter">// </span><span class="comment">&#20351;&#29992;autorelease&#19968;&#27425;&#23601;&#23558;&#21046;&#23450;&#23545;&#35937;&#25918;&#20837;pool&#20013;&#65292;&#25918;&#20960;&#27425;[pool drain]&#30340;&#26102;&#20505;&#23601;&#37322;&#25918;&#20960;&#27425;
</span>}

- (<span class="type">void</span>)<span class="function-name">setValue</span>:(<span class="type">TempValue</span> *)<span class="variable-name">value</span> {
  [_member autorelease];
  _member = [value retain];
}
</pre>
</td>
<tr></table>
</div>
<p>该原则遵循 Failed Self 的原则，虽然从性能上看有所损耗但是保证了代码质量。</p>
<h5>对象在函数中返回时，使用return [[object retain] autorelease]</h5>
<p>严格地说，是除 alloc/new/copy/mutableCopy 开头函数以外的函数中，有对象放回时，使用return [[object retain] autorelease]。</p>
<dl>
<dt><strong>我们结合下面的例子来说明，并总结出该问题的几种解决方案</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="keyword">@implementation</span> <span class="type">FooClass</span>

- (<span class="type">void</span>)<span class="function-name">setObject</span>:(<span class="type">MyObject</span> *)<span class="variable-name">object</span>;
 {
     <span class="comment-delimiter">// </span><span class="comment">&#36825;&#37324;&#25925;&#24847;&#27809;&#26377;&#20351;&#29992; autorelease&#65292;&#20197;&#20415;&#35828;&#26126;&#38382;&#39064;
</span>     [_object release];
     _object = [object retain];
 }

- (<span class="type">id</span>)<span class="function-name">object</span>;
 {
     <span class="keyword">return</span> _object;
 }

- (<span class="type">void</span>)<span class="function-name">dealloc</span>;
 {
     [_object release];
     [<span class="keyword">super</span> dealloc];
 }

<span class="keyword">@end</span>

<span class="keyword">@implementation</span> <span class="type">BarClass</span>

- (<span class="type">void</span>)<span class="function-name">doStuff</span>;
 {
    <span class="type">FooClass</span> * <span class="variable-name">foo</span> = [[<span class="type">FooClass</span> alloc] init];

    <span class="comment-delimiter">// </span><span class="comment">&#21019;&#24314;&#31532;&#19968;&#20010;&#23545;&#35937;&#65292;&#24341;&#29992;&#35745;&#25968; = 1
</span>    <span class="type">MyObject</span> * <span class="variable-name">firstObject</span> = [[<span class="type">MyObject</span> alloc] init];
    <span class="comment-delimiter">// </span><span class="comment">setObject&#20013;&#30001;&#20110; [object retain] &#65292;&#24341;&#29992;&#35745;&#25968; = 2
</span>    [foo setObject:firstObject];
    <span class="comment-delimiter">// </span><span class="comment">&#37322;&#25918;&#19968;&#27425;&#65292;&#24341;&#29992;&#35745;&#25968; = 1&#65307;&#36825;&#20043;&#21518;&#23545;&#35937;&#26377;&#27491;&#30830;&#30340;&#25152;&#26377;&#26435;&#23646;&#24615;
</span>    [firstObject release];

    <span class="comment-delimiter">// </span><span class="comment">&#36890;&#36807;&#38750; alloc/new/copy/mutableCopy &#24320;&#22836;&#20989;&#25968;&#24471;&#21040;&#23545;&#35937;
</span>    <span class="comment-delimiter">// </span><span class="comment">anObject &#25351;&#21521;&#31532;&#19968;&#20010;&#23545;&#35937;&#65292;&#20294;&#26159;&#24182;&#27809;&#26377;&#20854;&#25152;&#26377;&#26435;&#65292;&#23545;&#35937;&#24341;&#29992;&#35745;&#25968; = 1
</span>    <span class="type">MyObject</span> * <span class="variable-name">anObject</span> = [foo object];
    [anObject testMethod];

    <span class="comment-delimiter">// </span><span class="comment">&#21019;&#24314;&#31532;&#20108;&#20010;&#23545;&#35937;
</span>    <span class="type">MyObject</span> * <span class="variable-name">secondObject</span> = [[<span class="type">MyObject</span> alloc] init];
    <span class="comment-delimiter">// </span><span class="comment">setObject&#20013;&#30001;&#20110; [_object release]; &#31532;&#19968;&#20010;&#23545;&#35937;&#24341;&#29992;&#35745;&#25968; = 0&#65292;&#20869;&#23384;&#34987;&#37322;&#25918;
</span>    [foo setObject:secondObject];
    [secondObject release];

    <span class="comment-delimiter">// </span><span class="comment">&#31243;&#24207;&#22312;&#36825;&#37324;&#23849;&#28291;&#20102;&#65292;&#22240;&#20026; anObject &#25351;&#21521;&#20102;&#19968;&#20010;&#31354;&#22320;&#22336;
</span>    [anObject testMethod];
}

<span class="keyword">@end</span>
</pre>
</td>
<tr></table>
</div>
<p>从结论我们来看看该问题的几种可行的解决方案；各种方案中没有列出的代码与原先代码一致。</p>
<dl>
<dt><strong>生成对象时，使用autorelease</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="keyword">@implementation</span> <span class="type">BarClass</span>

- (<span class="type">void</span>)<span class="function-name">doStuff</span>;
 {
    <span class="type">FooClass</span> * <span class="variable-name">foo</span> = [[<span class="type">FooClass</span> alloc] init];

    <span class="type">MyObject</span> * <span class="variable-name">firstObject</span> = [[[<span class="type">MyObject</span> alloc] init] autorelease];
    [foo setObject:firstObject];

    <span class="type">MyObject</span> * <span class="variable-name">anObject</span> = [foo object];
    [anObject testMethod];

    <span class="type">MyObject</span> * <span class="variable-name">secondObject</span> = [[[<span class="type">MyObject</span> alloc] init] autorelease];
    [foo setObject:secondObject];

    [anObject testMethod];
}

<span class="keyword">@end</span>
</pre>
</td>
<tr></table>
</div>
<p>对象生成时，即被放入最近的 pool 中，不需要人为特殊的维护，对象的生命周期将被延续，出 <strong>{}</strong> 范围之时即对象释放之际。</p>
<dl>
<dt><strong>对象代入时，先autorelease后再retain</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">- (<span class="type">void</span>)<span class="function-name">setObject</span>:(<span class="type">MyObject</span> *)<span class="variable-name">object</span>;
 {
     [_object autorelease];
     _object = [object retain];
 }

- (<span class="type">id</span>)<span class="function-name">object</span>;
 {
     <span class="comment-delimiter">// </span><span class="comment">&#36981;&#24490;&#38750; alloc/new/copy/mutableCopy &#24320;&#22836;&#30340;&#20989;&#25968;&#65292;&#19981;&#36176;&#20104;&#25152;&#26377;&#26435;&#21407;&#21017;
</span>     <span class="keyword">return</span> _object;
 }
</pre>
</td>
<tr></table>
</div>
<p>同样的，对象被放入最近的 pool 中，第二次 setObject 后对象引用计数仍为1， pool 清空时才执行最后一次对象release，从而保证了代码的正确性。</p>
<dl>
<dt><strong>对象在函数中返回时，使用return [[object retain] autorelease];</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">- (<span class="type">void</span>)<span class="function-name">setObject</span>:(<span class="type">MyObject</span> *)<span class="variable-name">object</span>;
 {
     [_object release];
     _object = [object retain];
 }

- (<span class="type">id</span>)<span class="function-name">object</span>;
 {
     <span class="comment-delimiter">// </span><span class="comment">&#36981;&#24490;&#38750; alloc/new/copy/mutableCopy &#24320;&#22836;&#30340;&#20989;&#25968;&#65292;&#19981;&#36176;&#20104;&#25152;&#26377;&#26435;&#21407;&#21017;
</span>     <span class="keyword">return</span> [[_object retain] autorelease];
 }
</pre>
</td>
<tr></table>
</div>
<p>好不容易回到了本小节要说明的方法；可以看到这是从另一个角度解决了该问题：[foo object] 的时候保证引用计数是2，并将对象放入pool中维护。</p>
<p>总结上面上面3中方法，虽说是从不同角度入手解决了这个问题，但是基本原则不变，利用 NSAutoreleasePool 机制帮程序员维护代码，管理内存。</p>
<p>如果你觉得3种编码原则怎么搭配使用，在什么样的场合下选择比较麻烦，不要紧，都用就得了。我们牺牲的只是 NSAutoreleasePool 中的一些内存，一小许性能损失罢了，这总比我们的程序崩溃了强。</p>
<h3><a name="sec4" id="sec4"></a>ARC 诞生</h3>
<p class="first">ARC 是什么我不需要再解释，若有不明白，可以看<a href="http://www.yifeiyang.net/development-of-the-iphone-simply-1/">iPhone开发之深入浅出 (1) — ARC是什么</a>。</p>
<dl>
<dt><strong>ARC 严格遵守 Objective-C 内存管理的基本原则</strong></dt>
<dd></dd>
</dl>
<ul>
<li>自己生成的对象，那么既是其持有者</li>
<li>不是自己生成的对象，也可成为其持有者（一个对象可以被多个人持有）</li>
<li>如果不想持有对象的时候，必须释放其所有权</li>
<li>不能释放已不再持有所有权的对象</li>
</ul>
<p>并从编译器角度维护了该原则，比如如果不是 alloc/new/copy/mutableCopy 开头的函数，编译器会将生成的对象自动放入 autoReleasePool 中。如果是 __strong 修饰的变量，编译器会自动给其加上所有权。等等，详细，我们根据不同的关键字来看看编译器为我们具体做了什么。并从中总结出 ARC 的使用规则。</p>
<h4><a name="sec5" id="sec5"></a>__strong</h4>
<p class="first">我们先来看看用 __strong 修饰的变量，以及缺省隐藏的 __strong 情况。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">{
    <span class="comment-delimiter">/*</span><span class="comment">
     * &#29983;&#25104;&#23545;&#35937;&#24182;&#25317;&#26377;&#25152;&#26377;&#26435;
     */</span>
    <span class="type">id</span> __strong obj = [[<span class="type">NSObject</span> alloc] init];

    <span class="comment-delimiter">/*</span><span class="comment">
     * &#33258;&#24049;&#19968;&#30452;&#26159;&#25345;&#26377;&#23545;&#35937;&#29366;&#24577;
     */</span>
}
    <span class="comment-delimiter">/*</span><span class="comment">
     * &#21464;&#37327;&#20986;&#29983;&#21629;&#21608;&#26399;&#26102;&#65292;&#22833;&#21435;&#20840;&#37096;&#25152;&#26377;&#32773;&#65292;&#23545;&#35937;&#20869;&#23384;&#31354;&#38388;&#34987;&#37322;&#25918;
     */</span>
</pre>
</td>
<tr></table>
</div>
<p>这种情况毫无悬念，缺省使用 alloc/new/copy/mutableCopy 开头的函数也是这样的结果。并且在这里，编译器帮我们自动的调用了对象的 release 函数，不需要手工维护。再看看下面的情况。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">{
    <span class="comment-delimiter">/*</span><span class="comment">
     * &#29983;&#25104;&#23545;&#35937;&#20294;&#26159;&#24182;&#27809;&#26377;&#20854;&#25152;&#26377;&#26435;
     */</span>
    <span class="type">id</span> __strong obj = [<span class="type">NSMutableArray</span> array];

    <span class="comment-delimiter">/*</span><span class="comment">
     * &#30001;&#20110;&#21464;&#37327;&#22768;&#26126;&#26159;&#24378;&#24341;&#29992;&#65292;&#33258;&#24049;&#19968;&#30452;&#26159;&#25345;&#26377;&#23545;&#35937;&#29366;&#24577;
     * &#32534;&#35793;&#22120;&#26681;&#25454;&#20989;&#25968;&#21517;&#65292;&#20877;&#23558;&#35813;&#23545;&#35937;&#25918;&#20837; autoreleasepool &#20013;
     */</span>
}
    <span class="comment-delimiter">/*</span><span class="comment">
     * &#21464;&#37327;&#20986;&#29983;&#21629;&#21608;&#26399;&#26102;&#65292;&#22833;&#21435;&#20840;&#37096;&#25152;&#26377;&#32773;&#65292;&#23545;&#35937;&#20869;&#23384;&#31354;&#38388;&#34987;&#37322;&#25918;
     */</span>
</pre>
</td>
<tr></table>
</div>
<p>由上，虽然不是用 alloc/new/copy/mutableCopy 开头的函数得到的对象，由于是强参照，我们任然成为对象的持有者。而这，正是编译器帮我们做到的。</p>
<blockquote>
<p class="quoted">具体做的是什么呢？其实就是【对象在函数中返回时，使用return [[object retain] autorelease]】所描述的；如果你反汇编一下ARC生成的代码，可以看到这时会自动调用名为 objc_retainAutoreleaseReturnValue 的函数，而其作用和 [[object retain] autorelease] 一致。编译器通过函数名分析，如果不是 alloc/new/copy/mutableCopy 开头的函数，自动加入了这段代码。</p>
</blockquote>
<p>另外，缺省 __strong 修饰的变量，对象代入的时候也正确地保证对象所有者规则；代入新对象时，自动释放旧对象的参照，代入nil的时候，表示释放当前对象的强参照。</p>
<h4><a name="sec6" id="sec6"></a>__weak</h4>
<p class="first">虽然大部分场合，大部分问题使用 __strong 来编码就足够了；但是为了解决循环参照的问题 __weak 关键字修饰【弱参照】变量就发挥了左右。关于循环参照的问题，准备在以后的博文中介绍；今天，主要看看编译器在背后怎么处理 __weak 变量的。</p>
<p>__weak 声明的变量其实是被放入一个weak表中，该表和引用计数的表格类似，是一个Hash表，都是以对象的内存地址做key，同时，针对一个对象地址的key，可以同时对应多个变量的地址。</p>
<dl>
<dt><strong>当一个 __weak 所指对象被释放时，系统按下面步骤来处理</strong></dt>
<dd></dd>
</dl>
<ul>
<li>从weak表中，通过对象地址（key）找到entry</li>
<li>将entry中所有指向该对象的变量设为nil</li>
<li>从weak表中删除该entry</li>
<li>从对象引用计数表中删除对象entry(通过通过对象地址找到)</li>
</ul>
<p>另外，当使用 __weak 修饰的变量的时候，变量将放入 autoreleasepool 中，并且用几次放几次。比如下面的简单例子。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">{
    <span class="type">id</span> __weak o = obj;
    NSLog(@<span class="string">"1 %@"</span>, o);
    NSLog(@<span class="string">"2 %@"</span>, o);
    NSLog(@<span class="string">"3 %@"</span>, o);
    NSLog(@<span class="string">"4 %@"</span>, o);
    NSLog(@<span class="string">"5 %@"</span>, o);
}
</pre>
</td>
<tr></table>
</div>
<p>这里我们用了5次，那么pool中就被登录了5次；从效率上考虑这样当然不是很好，可以通过代入 __strong 修饰的强参照变量来避开这个问题。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">{
    <span class="type">id</span> __weak o = obj;
    <span class="type">id</span> <span class="variable-name">temp</span> = o;
    NSLog(@<span class="string">"1 %@"</span>, temp);
    NSLog(@<span class="string">"2 %@"</span>, temp);
    NSLog(@<span class="string">"3 %@"</span>, temp);
    NSLog(@<span class="string">"4 %@"</span>, temp);
    NSLog(@<span class="string">"5 %@"</span>, temp);
}
</pre>
</td>
<tr></table>
</div>
<p>另外，还有通过重载 allowsWeakReference/retainWeakReference 函数来限制 __weak 声明变量使用回数的方法，毕竟不在本次讨论范畴之内，就此省略。</p>
<p>话说回来，为什么使用弱参照变量的时候，要将其放入 autoreleasepool 中呢？想想弱参照的定义就应该明白了 &mdash;- 如果在访问弱参照对象时，该对象被释放了怎么办，程序不就崩溃了嘛；所以为了解决该问题，又再一次用到了 pool。</p>
<h4><a name="sec7" id="sec7"></a>__autoreleasing</h4>
<p class="first">虽然上面还没有降到该关键字，但是编译器在很多时候已经用到了 autoreleasepool。比如非 alloc/new/copy/mutableCopy 开头的函数返回一个对象的时候，又比如使用一个 __weak 声明的变量的时候。</p>
<p>实际上，写ARC代码的时候，明示 __autoreleasing 声明变量和明示 __strong 声明变量一样基本上没有，因为编译器已经为我们做了很多，很智能了（前提是我们要按ARC的规则写代码）。</p>
<p>还有一种编译器缺省使用 __autoreleasing 关键字声明变量的时候：对象指针类型。比如下面的对应关系。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">   <span class="type">id</span> *<span class="variable-name">obj</span> == <span class="type">id</span> __autoreleasing *obj
   NSObject **obj == NSObject * __autoreleasing *obj
</pre>
</td>
<tr></table>
</div>
<p>所以，下面两个函数的是等价的。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">-(<span class="type">BOOL</span>)<span class="function-name">performOperationWithError</span>:(<span class="type">NSError</span> **)<span class="variable-name">error</span>;

-(<span class="type">BOOL</span>)<span class="function-name">performOperationWithError</span>:(<span class="type">NSError</span> * __autoreleasing *)<span class="variable-name">error</span>;
</pre>
</td>
<tr></table>
</div>
<p>像下面的函数调用，为什么是可行的呢？</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">NSError __strong *error = <span class="constant">nil</span>;
<span class="type">BOOL</span> <span class="variable-name">result</span> = [obj performOperationWithError:&amp;error];
</pre>
</td>
<tr></table>
</div>
<p>其实，编译器是这样解释这段代码的。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">NSError __strong *error = <span class="constant">nil</span>;
NSError __autoreleasing *tmp = error;
<span class="type">BOOL</span> <span class="variable-name">result</span> = [obj performOperationWithError:&amp;tmp];
error = tmp;
</pre>
</td>
<tr></table>
</div>
<p>那么我们这样声明函数不就可以了吗？</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">-(<span class="type">BOOL</span>)<span class="function-name">performOperationWithError</span>:(<span class="type">NSError</span> * __strong *)<span class="variable-name">error</span>;
</pre>
</td>
<tr></table>
</div>
<p>答案是肯定的，你可以这样做，编译是可以通过，但你违反了非 alloc/new/copy/mutableCopy 开头的函数，不返回对象持有权的原则。这里是没有问题了，但也许影响到其他地方NG。</p>
<h4><a name="sec8" id="sec8"></a>ARC 规则</h4>
<dl>
<dt><strong>结合上面的讲解，我想你也应该能够总结出来使用ARC时的规则</strong></dt>
<dd></dd>
</dl>
<p>（这里只列出本讲中涉及的内容，其他的内容以后总结）</p>
<ul>
<li>代码中不能使用retain, release, retain, autorelease</li>
<li>不能使用NSAllocateObject, NSDeallocateObject</li>
<li>不能使用NSAutoReleasePool、而需要@autoreleasepool块</li>
<li>严守内存管理相关函数命名规则</li>
</ul>
<p>关于函数命名，伴随ARC的导入，还有一系列函数的定义也被严格定义了，那就是以 init 开头的函数。init 函数作为alloc生成对象的初期化函数，需要按原样直接传递对象给调用段，所以下面的声明是OK的。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">-(<span class="type">id</span>)<span class="function-name">initWithObject</span>:(<span class="type">id</span>)<span class="variable-name">obj</span>;
</pre>
</td>
<tr></table>
</div>
<p>而下面的是NG的。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">-(<span class="type">void</span>)<span class="function-name">initWithObject</span>;
</pre>
</td>
<tr></table>
</div>
<p>不过声明为 -(void) initialize; 是没有问题的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/development-of-the-iphone-simply-3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>iPhone开发技巧之发布篇（7）&#8212;  制作自己的Cydia发布源</title>
		<link>http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-7/</link>
		<comments>http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-7/#comments</comments>
		<pubDate>Fri, 20 Jan 2012 02:07:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[发布篇]]></category>
		<category><![CDATA[开发技巧]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[muse]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/iphone%e5%bc%80%e5%8f%91%e6%8a%80%e5%b7%a7%e4%b9%8b%e5%8f%91%e5%b8%83%e7%af%87%ef%bc%887%ef%bc%89-%e5%88%b6%e4%bd%9c%e8%87%aa%e5%b7%b1%e7%9a%84cydia%e5%8f%91%e5%b8%83%e6%ba%90/</guid>
		<description><![CDATA[<div class="contents">
<dt> <a href="#sec1">Linux Debian</a> </dt>
<dt> <a href="#sec2">脚本工具</a> </dt>
<dt> <a href="#sec3">制作deb文件</a> </dt>
<dt> <a href="#sec4">制作Repository</a> </dt>
</div>
<p style="text-align: center;"><strong>iPhone开发技巧之发布篇（7）— 制作自己的Cydia发布源</strong></p>
<p>针对越狱的iPhone，大家肯定都知道用Cydia来安装各种免费应用的经历。Cydia中是否能指定我们自己的发布源，让我们自己随心所欲地[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-7/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt> <a href="#sec1">Linux Debian</a> </dt>
<dt> <a href="#sec2">脚本工具</a> </dt>
<dt> <a href="#sec3">制作deb文件</a> </dt>
<dt> <a href="#sec4">制作Repository</a> </dt>
</div>
<p style="text-align: center;"><strong>iPhone开发技巧之发布篇（7）— 制作自己的Cydia发布源</strong></p>
<p>针对越狱的iPhone，大家肯定都知道用Cydia来安装各种免费应用的经历。Cydia中是否能指定我们自己的发布源，让我们自己随心所欲地发布程序给别人呢？答案是肯定的，我们只要创建Cydia的发布repository即可。</p>
<h4><a id="sec1" name="sec1"></a>Linux Debian</h4>
<p class="first">首先，我们需要有<a href="http://www.debian.org">Linux Debian</a>或者<a href="http://www.ubuntulinux.jp/products/GetUbuntu">Ubuntu</a>系统来制作Cydia的发布源。如果你没有Linux Debian系统的机器，不要紧，可以随时用VMware Player或VirtualBox虚拟机来安装一个。</p>
<p>安装的时候用i386的iso即可，如果是64bit的OS用amd64的image来安装。安装的时候，选择Graphical Install，其他缺省安装。</p>
<h4><a id="sec2" name="sec2"></a>脚本工具</h4>
<p class="first">下载下面两个脚本文件到本地，并加上可执行属性。</p>
<ul>
<li>下载<a href="http://www.yifeiyang.net/download/iphone/cydia/dpkg-scanpackages">dpkg-scanpackages</a>到/usr/bin</li>
<li>下载<a href="http://www.yifeiyang.net/download/iphone/cydia/dpkg-gettext.pl">dpkg-gettext.pl</a>到/etc/perl。</li>
</ul>
<h4><a id="sec3" name="sec3"></a>制作deb文件</h4>
<dl>
<dt><strong>以名称为MyProgram的程序微粒，首先在home目录下建立下面的目录结构</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table border="0" width="100%">
<tbody>
<tr>
<td class="line_numbers">
<pre>1
2
3
4
5
6
7
8
9
10</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">Cydia
   +- MyProgram
      +- DEBIAN
      |  +- control
      +- var
         +- mobile
            +- MyProgram.app
                +- Info.plist
                +- MyProgram
                +- icon.png</pre>
</td>
</tr>
</tbody>
</table>
</div>
<p>可以看出来，我们需要把程序MyProgram安装到/var/mobile/中去.</p>
<p>DEBIAN目录下面有一个名为control的文件，我们来看看它的内容。</p>
<div class="codebox">
<table border="0" width="100%">
<tbody>
<tr>
<td class="line_numbers">
<pre>1
2
3
4
5
6
7
8
9</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">Package: net.yifeiyang.MyProgram
Name: MyProgram
Version: 1.0.4-1
Architecture: iphoneos-arm
Description: test text.
Homepage: http://www.yifeiyang.net
Maintainer: YIFEIYANG &lt;kane_yj@hotmail.com&gt;
Author: YIFEIYANG &lt;kane_yj@hotmail.com&gt;
Section: Games</pre>
</td>
</tr>
</tbody>
</table>
</div>
<table class="muse-table" border="2" cellpadding="5">
<thead>
<tr>
<th>名称</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>Package</td>
<td>唯一标示Package的名称，一般用「域名.Package名」</td>
</tr>
<tr>
<td>Name</td>
<td>程序的名称</td>
</tr>
<tr>
<td>Version</td>
<td>程序版本，不能使用字母</td>
</tr>
<tr>
<td>Architecture</td>
<td>固定为iphoneos-arm</td>
</tr>
<tr>
<td>Description</td>
<td>程序概要说明，将显示在Cydia的说明页内</td>
</tr>
<tr>
<td>Homepage</td>
<td>程序网页</td>
</tr>
<tr>
<td>Maintainer</td>
<td>维护者名称，邮箱</td>
</tr>
<tr>
<td>Author</td>
<td>作者，邮箱</td>
</tr>
<tr>
<td>Section</td>
<td>所属类型，设定了之后，程序名旁边将显示具体的icon</td>
</tr>
</tbody>
</table>
<dl>
<dt><strong>编译在制作DEB文件很简单，只需要</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table border="0" width="100%">
<tbody>
<tr>
<td class="line_numbers">
<pre>1
2</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="builtin">cd</span> ~/Cydia
dpkg-deb –b MyProgram</pre>
</td>
</tr>
</tbody>
</table>
</div>
<p>这之后，我们就可以试试该deb文件，用iFile将文件上传到iPhone上并安装，查看/var/mobile/下的情况。这里我们可以使用<a href="http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-6/">iPhone开发技巧之发布篇（6）— 不需Developper认证的真机调试方法</a>中的方法制作我们的程序，从而不需要验证信息。</p>
<h4><a id="sec4" name="sec4"></a>制作Repository</h4>
<dl>
<dt><strong>每次追加新的deb文件，或者是deb文件有更新时，需要做下面两步</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table border="0" width="100%">
<tbody>
<tr>
<td class="line_numbers">
<pre>1
2</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">dpkg-scanpackages –m . /dev/null &gt;Packages
bzip2 Packages</pre>
</td>
</tr>
</tbody>
</table>
</div>
<p>OK了，接下来我们就可以把Packages.bz2和MyProgram.deb这两个文件上传到你的web服务器中，然后把地址作为Source添加到Cydia中就搞定了。如果没有web服务器也没有关系，可以使用Dropbox等共享网盘，得到唯一的一个URL即可。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-7/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>iPhone开发之深入浅出 (2) &#8212; ARC之@property使用</title>
		<link>http://www.yifeiyang.net/development-of-the-iphone-simply-2/</link>
		<comments>http://www.yifeiyang.net/development-of-the-iphone-simply-2/#comments</comments>
		<pubDate>Wed, 04 Jan 2012 00:22:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[ARC]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[深入浅出]]></category>
		<category><![CDATA[muse]]></category>
		<category><![CDATA[Objective-C]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/iphone%e5%bc%80%e5%8f%91%e4%b9%8b%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%ba-2-arc%e4%b9%8bproperty%e4%bd%bf%e7%94%a8/</guid>
		<description><![CDATA[<dt> <a href="#sec1">所有者属性</a> </dt>
<dt> <a href="#sec2">读写相关的属性 (readwrite, readonly)</a> </dt>

<p style="text-align: center;"> <strong>iPhone开发之深入浅出 (2) &#8212; ARC之@property使用</strong> </p>
<p>上一回我们学到了一些ARC的基本概念，这一次我们来看看ARC对@property的使用规则有何影响。</p>
<h4><a name="sec1" id="sec1"></a>所有者属性</h4>
<p class="first">我们先来看看与所[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/development-of-the-iphone-simply-2/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<dt> <a href="#sec1">所有者属性</a> </dt>
<dt> <a href="#sec2">读写相关的属性 (readwrite, readonly)</a> </dt>
</p></div>
<p style="text-align: center;"> <strong>iPhone开发之深入浅出 (2) &mdash; ARC之@property使用</strong> </p>
<p>上一回我们学到了一些ARC的基本概念，这一次我们来看看ARC对@property的使用规则有何影响。</p>
<h4></span><a name="sec1" id="sec1"></a>所有者属性</h4>
<p class="first">我们先来看看与所有权有关系的属性，关键字间的对应关系。</p>
<table class="muse-table" border="2" cellpadding="5">
<thead>
<tr>
<th>属性值</th>
<th>关键字</th>
<th>所有权</th>
</tr>
</thead>
<tbody>
<tr>
<td>strong</td>
<td>__strong</td>
<td>有</td>
</tr>
<tr>
<td>weak</td>
<td>__weak</td>
<td>无</td>
</tr>
<tr>
<td>unsafe_unretained</td>
<td>__unsafe_unretained</td>
<td>无</td>
</tr>
<tr>
<td>copy</td>
<td>__strong</td>
<td>有</td>
</tr>
<tr>
<td>assign</td>
<td>__unsafe_unretained</td>
<td>无</td>
</tr>
<tr>
<td>retain</td>
<td>__strong</td>
<td>有</td>
</tr>
</tbody>
</table>
<dl>
<dt><strong>strong</strong></dt>
<dd>
<p>该属性值对应 __strong 关键字，即该属性所声明的变量将成为对象的持有者。</p>
</dd>
<dt><strong>weak</strong></dt>
<dd>
<p>该属性对应 __weak 关键字，与 __weak 定义的变量一致，该属性所声明的变量将没有对象的所有权，并且当对象被破弃之后，对象将被自动赋值nil。</p>
<p>并且，delegate 和 Outlet 应该用 weak 属性来声明。同时，如上一回介绍的 iOS 5 之前的版本是没有 __weak 关键字的，所以 weak 属性是不能使用的。这种情况我们使用 unsafe_unretained。</p>
</dd>
<dt><strong>unsafe_unretained</strong></dt>
<dd>
<p>等效于__unsafe_unretaind关键字声明的变量；像上面说明的，iOS 5之前的系统用该属性代替 weak 来使用。</p>
</dd>
<dt><strong>copy</strong></dt>
<dd>
<p>与 strong 的区别是声明变量是拷贝对象的持有者。</p>
</dd>
<dt><strong>assign</strong></dt>
<dd>
<p>一般Scalar Varible用该属性声明，比如,int, BOOL。</p>
</dd>
<dt><strong>retain</strong></dt>
<dd>
<p>该属性与 strong 一致；只是可读性更强一些。</p>
</dd>
</dl>
<h4></span><a name="sec2" id="sec2"></a>读写相关的属性 (readwrite, readonly)</h4>
<p class="first">读写相关的属性有 readwrite 和 readonly 两种，如果使用ARC之后，我么需要注意一下 readonly 属性的使用。</p>
<p>比如下面的变量声明。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">@property (nonatomic, readonly) <span class="type">NSString</span> *<span class="variable-name">name</span>;
</pre>
</td>
<tr></table>
</div>
<p>一般声明为 readonly 的变量按理说应该不需要持有所有权了，但是在ARC有效的情况下，将出现下面的错误信息 ：</p>
<blockquote>
<p class="quoted">“ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute”</p>
</blockquote>
<p><strong>如果定义了ARC有效，那么必须要有所有者属性的定义</strong>；所以我们的代码改成这样，就OK了</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">@property (nonatomic, strong, readonly) <span class="type">NSString</span> *<span class="variable-name">name</span>;
</pre>
</td>
<tr></table>
</div>
<p>不过有一点，Scalar Varible的变量缺省都有 assign 的属性定义，所以不需要给他们单独的明示声明了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/development-of-the-iphone-simply-2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>iPhone开发之深入浅出 (1) &#8212; ARC是什么</title>
		<link>http://www.yifeiyang.net/development-of-the-iphone-simply-1/</link>
		<comments>http://www.yifeiyang.net/development-of-the-iphone-simply-1/#comments</comments>
		<pubDate>Sun, 01 Jan 2012 01:04:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[ARC]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[深入浅出]]></category>
		<category><![CDATA[Objective-C]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/iphone%e5%bc%80%e5%8f%91%e4%b9%8b%e6%b7%b1%e5%85%a5%e6%b5%85%e5%87%ba-1-arc%e6%98%af%e4%bb%80%e4%b9%88/</guid>
		<description><![CDATA[<div class="contents">
<dt> <a href="#sec1">ARC是什么</a> </dt>
<dd>
<dl>
<dt> <a href="#sec2">变化点</a> </dt>
<dt> <a href="#sec3">使用ARC的好处</a> </dt>
<dt> <a href="#sec4">不好的地方</a> </dt>
</dl>
</dd>
<dt> <a href="#sec5">ARC基本规则</a> </dt>
<dd>
<dl>
<dt> <a href="#sec6">Objective-C对象</a> </dt>
<dt> <a href="#sec7">引用关键字</a> </dt>
</dl>
</dd>
<dt> <a href="#sec8">总结</a> </dt>
</div>
<p style="text-align: center;"> <strong>iPhone开发之深入浅出 (1) &#8212; ARC是什么</strong> </p>
<p>新年伊始，万象更新。新一年开始，我们来更加深入了解一下iPhon[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/development-of-the-iphone-simply-1/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt> <a href="#sec1">ARC是什么</a> </dt>
<dd>
<dl>
<dt> <a href="#sec2">变化点</a> </dt>
<dt> <a href="#sec3">使用ARC的好处</a> </dt>
<dt> <a href="#sec4">不好的地方</a> </dt>
</dl>
</dd>
<dt> <a href="#sec5">ARC基本规则</a> </dt>
<dd>
<dl>
<dt> <a href="#sec6">Objective-C对象</a> </dt>
<dt> <a href="#sec7">引用关键字</a> </dt>
</dl>
</dd>
<dt> <a href="#sec8">总结</a> </dt>
</p></div>
<p style="text-align: center;"> <strong>iPhone开发之深入浅出 (1) &mdash; ARC是什么</strong> </p>
<p>新年伊始，万象更新。新一年开始，我们来更加深入了解一下iPhone开发的内部。作为开始，我们先来了解一下ARC。</p>
<h3><a name="sec1" id="sec1"></a>ARC是什么</h3>
<p class="first">ARC是iOS 5推出的新功能，全称叫 ARC(Automatic Reference Counting)。简单地说，就是代码中自动加入了retain/release，原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。</p>
<p>该机能在 iOS 5/ Mac OS X 10.7 开始导入，利用 Xcode4.2 可以使用该机能。简单地理解ARC，就是通过指定的语法，让编译器(LLVM 3.0)在编译代码时，自动生成实例的引用计数管理部分代码。有一点，ARC并不是GC，它只是一种代码静态分析（Static Analyzer）工具。</p>
<h4><a name="sec2" id="sec2"></a>变化点</h4>
<p class="first">通过一小段代码，我们看看使用ARC前后的变化点。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="keyword">@interface</span> <span class="type">NonARCObject</span> : <span class="type">NSObject</span> {
    <span class="type">NSString</span> *<span class="variable-name">name</span>;
}
-(<span class="type">id</span>)<span class="function-name">initWithName</span>:(<span class="type">NSString</span> *)<span class="variable-name">name</span>;
<span class="keyword">@end</span>

<span class="keyword">@implementation</span> <span class="type">NonARCObject</span>
-(<span class="type">id</span>)<span class="function-name">initWithName</span>:(<span class="type">NSString</span> *)<span class="variable-name">newName</span> {
    <span class="keyword">self</span> = [<span class="keyword">super</span> init];
    <span class="keyword">if</span> (<span class="keyword">self</span>) {
        name = [newName retain];
    }
    <span class="keyword">return</span> <span class="keyword">self</span>;
}

-(<span class="type">void</span>)<span class="function-name">dealloc</span> {
    [name release];
    [<span class="type">Super</span> dealloc];
}
<span class="keyword">@end</span>
</pre>
</td>
<tr></table>
</div>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="keyword">@interface</span> <span class="type">ARCObject</span> : <span class="type">NSObject</span> {
    <span class="type">NSString</span> *<span class="variable-name">name</span>;
}
-(<span class="type">id</span>)<span class="function-name">initWithName</span>:(<span class="type">NSString</span> *)<span class="variable-name">name</span>;
<span class="keyword">@end</span>

<span class="keyword">@implementation</span> <span class="type">ARCObject</span>
-(<span class="type">id</span>)<span class="function-name">initWithName</span>:(<span class="type">NSString</span> *)<span class="variable-name">newName</span> {
    <span class="keyword">self</span> = [<span class="keyword">super</span> init];
    <span class="keyword">if</span> (<span class="keyword">self</span>) {
        name = newName;
    }
    <span class="keyword">return</span> <span class="keyword">self</span>;
}
<span class="keyword">@end</span>
</pre>
</td>
<tr></table>
</div>
<dl>
<dt><strong>我们之前使用Objective-C中内存管理规则时，往往采用下面的准则</strong></dt>
<dd></dd>
</dl>
<ul>
<li>生成对象时，使用autorelease</li>
<li>对象代入时，先autorelease后再retain</li>
<li>对象在函数中返回时，使用return [[object retain] autorelease];</li>
</ul>
<p>而使用ARC后，我们可以不需要这样做了，甚至连最基础的release都不需要了。</p>
<h4><a name="sec3" id="sec3"></a>使用ARC的好处</h4>
<p class="first">使用ARC有什么好处呢？</p>
<ul>
<li>看到上面的例子，大家就知道了，以后写Objective-C的代码变得简单多了，因为我们不需要担心烦人的内存管理，担心内存泄露了</li>
<li>代码的总量变少了，看上去清爽了不少，也节省了劳动力</li>
<li>代码高速化，由于使用编译器管理引用计数，减少了低效代码的可能性</li>
</ul>
<h4><a name="sec4" id="sec4"></a>不好的地方</h4>
<ul>
<li>记住一堆新的ARC规则 &mdash; 关键字及特性等需要一定的学习周期</li>
<li>一些旧的代码，第三方代码使用的时候比较麻烦；修改代码需要工数，要么修改编译开关</li>
</ul>
<p>关于第二点，由于 XCode4.2 中缺省ARC就是 ON 的状态，所以编译旧代码的时候往往有&quot;Automatic Reference Counting Issue&quot;的错误信息。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/ARC/error.png" rel="lightbox[442]"><br />
<img src="http://www.yifeiyang.net/images/iphone/ARC/error.png" alt="ARC_error" /></a>
</p>
</div>
<p>这个时候，可以将项目编译设置中的“Objectice-C Auto Reference Counteting”设为NO。如下所示。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/ARC/ARC_Xcode1.png" rel="lightbox[442]"><br />
<img src="http://www.yifeiyang.net/images/iphone/ARC/ARC_Xcode1.png" alt="ARC_Xcode" /></a>
</p>
</div>
<p>如果只想对某个.m文件不适应ARC，可以只针对该类文件加上 -fno-objc-arc 编译FLAGS，如下图。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/ARC/ARC_Xcode2.png" rel="lightbox[442]"><br />
<img src="http://www.yifeiyang.net/images/iphone/ARC/ARC_Xcode2.png" alt="ARC_Xcode" /></a>
</p>
</div>
<h3><a name="sec5" id="sec5"></a>ARC基本规则</h3>
<ul>
<li>retain, release, autorelease, dealloc由编译器自动插入，不能在代码中调用</li>
<li>dealloc虽然可以被重载，但是不能调用[super dealloc]</li>
</ul>
<p>由于ARC并不是GC，并需要一些规则让编译器支持代码插入，所以必须清楚清楚了这些规则后，才能写出健壮的代码。</p>
<h4><a name="sec6" id="sec6"></a>Objective-C对象</h4>
<p class="first">ObjectiveC中的对象，有强参照(Strong reference)和弱参照(Weak reference)之分，当需要保持其他对象的时候，需要retain以确保对象引用计数加1。对象的持有者(owner)只要存在，那么该对象的强参照就一直存在。</p>
<dl>
<dt><strong>对象处理的基本规则是</strong></dt>
<dd></dd>
</dl>
<ul>
<li>只要对象的持有者存在（对象被强参照），那么就可以使用该对象</li>
<li>对象失去了持有者后，即被破弃</li>
</ul>
<h5>强参照 (Strong reference)</h5>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/ARC/ARC_outline_strong.JPG" rel="lightbox[442]"><br />
<img src="http://www.yifeiyang.net/images/iphone/ARC/ARC_outline_strong.JPG" alt="ARC_outline_strong" /></a>
</p>
</div>
<dl>
<dt><strong>(s1)</strong></dt>
<dd></dd>
</dl>
<p>firstName作为”natsu”字符串对象的最初持有者，是该NSString类型对象的Strong reference。</p>
<dl>
<dt><strong>(s2)</strong></dt>
<dd></dd>
</dl>
<p>这里将firstName代入到aName中，即aName也成为了@”natsu”字符串对象的持有者，对于该对象，aName也是Strong reference。</p>
<dl>
<dt><strong>(s3)</strong></dt>
<dd></dd>
</dl>
<p>这里，改变firstName的内容。生成新的字符串对象”maki”。这时候firstName成为”maki”的持有者，而@”natsu”的持有者只有aName。每个字符串对象都有各自的持有者，所以它们都在内存中都存在。</p>
<dl>
<dt><strong>(s4)</strong></dt>
<dd></dd>
</dl>
<p>追加新的变量otherName, 它将成为@”maki”对象的另一个持有者。即NSString类型对象的Strong reference。</p>
<dl>
<dt><strong>(s5)</strong></dt>
<dd></dd>
</dl>
<p>将otherName代入到aName，这时，aName将成为@”maki”字符串对象的持有者。而对象@”natsu”已经没有持有者了，该对象将被破弃。</p>
<h5>弱参照 (Weak reference)</h5>
<p>接下来我们来看看弱参照 (Weak reference) 的使用方式。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/ARC/ARC_outline_weak.png" rel="lightbox[442]"><br />
<img src="http://www.yifeiyang.net/images/iphone/ARC/ARC_outline_weak.png" alt="ARC_outline_weak" /></a>
</p>
</div>
<dl>
<dt><strong>(w1)</strong></dt>
<dd></dd>
</dl>
<p>与强参照方式同样，firstName作为字符串对象@”natsu”的持有者存在。即是该NSString类型对象的Strong reference。</p>
<dl>
<dt><strong>(w2)</strong></dt>
<dd></dd>
</dl>
<p>使用关键字__weak，声明弱参照weakName变量，将firstName代入。这时weakName虽然参照@”natsu”，但仍是Weak reference。即weakName虽然能看到@”natsu”，但不是其持有者。</p>
<dl>
<dt><strong>(w3)</strong></dt>
<dd></dd>
</dl>
<p>firstName指向了新的对象@”maki”，成为其持有者，而对象@”natsu”因为没有了持有者，即被破弃。同时weakName变量将被自动代入nil。</p>
<h4><a name="sec7" id="sec7"></a>引用关键字</h4>
<p class="first">ARC中关于对象的引用参照，主要有下面几关键字。使用strong, weak, autoreleasing限定的变量会被隐式初始化为nil。</p>
<dl>
<dt><strong>__strong</strong></dt>
<dd></dd>
</dl>
<p>变量声明缺省都带有__strong关键字，如果变量什么关键字都不写，那么缺省就是强参照。</p>
<dl>
<dt><strong>__weak</strong></dt>
<dd></dd>
</dl>
<p>上面已经看到了，这是弱参照的关键字。该概念是新特性，从 iOS 5/ Mac OS X 10.7 开始导入。由于该类型不影响对象的生命周期，所以如果对象之前就没有持有者，那么会出现刚创建就被破弃的问题，比如下面的代码。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">NSString __weak *string = [[<span class="type">NSString</span> alloc] initWithFormat:@<span class="string">"First Name: %@"</span>, [<span class="keyword">self</span> firstName]];
NSLog(@<span class="string">"string: %@"</span>, string); <span class="comment-delimiter">//</span><span class="comment">&#27492;&#26102; string&#20026;&#31354;
</span></pre>
</td>
<tr></table>
</div>
<p>如果编译设定OS版本 Deployment Target 设定为这比这低的版本，那么编译时将报错(The current deployment target does not support automated __weak references)，这个时候，我们可以使用下面的__unsafe_unretained。</p>
<p>弱参照还有一个特征，即当参数对象失去所有者之后，变量会被自动付上nil (Zeroing)。</p>
<dl>
<dt><strong>__unsafe_unretained</strong></dt>
<dd></dd>
</dl>
<p>该关键字与__weak一样，也是弱参照，与__weak的区别只是是否执行nil赋值(Zeroing)。但是这样，需要注意变量所指的对象已经被破弃了，地址还还存在，但内存中对象已经没有了。如果还是访问该对象，将引起「BAD_ACCESS」错误。</p>
<dl>
<dt><strong>__autoreleasing</strong></dt>
<dd></dd>
</dl>
<p>该关键字使对像延迟释放。比如你想传一个未初始化的对像引用到一个方法当中，在此方法中实例化此对像，那么这种情况可以使用__autoreleasing。他被经常用于函数有值参数返回时的处理，比如下面的例子。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">- (<span class="type">void</span>) <span class="function-name">generateErrorInVariable</span>:(__autoreleasing NSError **)<span class="variable-name">paramError</span> {
    ....
    *paramError = [[<span class="type">NSError</span> alloc] initWithDomain:@<span class="string">"MyApp"</span> code:1 userInfo:errorDictionary];
}

....
{
    <span class="type">NSError</span> *<span class="variable-name">error</span> = <span class="constant">nil</span>;
    [<span class="keyword">self</span> generateErrorInVariable:&amp;error];
    NSLog(@<span class="string">"Error = %@"</span>, error);
}
</pre>
</td>
<tr></table>
</div>
<p>又如函数的返回值是在函数中申请的，那么希望释放是在调用端时，往往有下面的代码。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">-(<span class="type">NSString</span> *)<span class="function-name">stringTest</span>
{
    <span class="type">NSString</span> *<span class="variable-name">retStr</span> = [<span class="type">NSString</span> stringWithString:@<span class="string">"test"</span>];

    <span class="keyword">return</span> [[retStr retain] autorelease];
}

<span class="comment-delimiter">// </span><span class="comment">&#20351;&#29992;ARC
</span>
-(<span class="type">NSString</span> *)<span class="function-name">stringTest</span>
{
    __autoreleasing NSString *retStr = [<span class="type">NSString</span> alloc] initWithString:@<span class="string">"test"</span>];

    <span class="keyword">return</span> retStr;
}
</pre>
</td>
<tr></table>
</div>
<p>即当方法的参数是id*，且希望方法返回时对象被autoreleased，那么使用该关键字。</p>
<h3><a name="sec8" id="sec8"></a>总结</h3>
<dl>
<dt><strong>今天，我们看到了基本的ARC使用规则</strong></dt>
<dd></dd>
</dl>
<ul>
<li>代码中不能使用retain, release, retain, autorelease</li>
<li>不重载dealloc（如果是释放对象内存以外的处理，是可以重载该函数的，但是不能调用[super dealloc]）</li>
<li>不能使用NSAllocateObject, NSDeallocateObject</li>
<li>不能在C结构体中使用对象指针</li>
<li>id与void *间的如果cast时需要用特定的方法（__bridge关键字）</li>
<li>不能使用NSAutoReleasePool、而需要@autoreleasepool块</li>
<li>不能使用“new”开始的属性名称 （如果使用会有下面的编译错误”Property’s synthesized getter follows Cocoa naming convention for returning ‘owned’ objects”）</li>
</ul>
<p>今后，我们将更加深入ARC，学习其更多的特性。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/development-of-the-iphone-simply-1/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>iPhone开发技巧之发布篇（6）&#8212;   不需Developper认证的真机调试方法</title>
		<link>http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-6/</link>
		<comments>http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-6/#comments</comments>
		<pubDate>Sun, 25 Dec 2011 00:05:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[发布篇]]></category>
		<category><![CDATA[开发技巧]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[linux]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/iphone%e5%bc%80%e5%8f%91%e6%8a%80%e5%b7%a7%e4%b9%8b%e5%8f%91%e5%b8%83%e7%af%87%ef%bc%886%ef%bc%89-%e4%b8%8d%e9%9c%80developper%e8%ae%a4%e8%af%81%e7%9a%84%e7%9c%9f%e6%9c%ba%e8%b0%83%e8%af%95/</guid>
		<description><![CDATA[<div class="contents">
<dt> <a href="#sec1">制作证明书</a> </dt>
<dt> <a href="#sec2">程序设置</a> </dt>
<dt> <a href="#sec3">署名</a> </dt>
<dt> <a href="#sec4">验证</a> </dt>
</div>
<p style="text-align: center;"> <strong>iPhone开发技巧之发布篇（6）&#8212; 不需Developper认证的真机调试方法</strong> </p>
<p>以前，<a href="http://www.yifeiyang.net/introduction-to-iphone-development-10-devices-running-the-program/">iPhone开发入门（10）--- 设备上运行程序</a>中介绍了正式注册iPhone/iOS Developer Program后在设备运行程序的方[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-6/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt> <a href="#sec1">制作证明书</a> </dt>
<dt> <a href="#sec2">程序设置</a> </dt>
<dt> <a href="#sec3">署名</a> </dt>
<dt> <a href="#sec4">验证</a> </dt>
</p></div>
<p style="text-align: center;"> <strong>iPhone开发技巧之发布篇（6）&mdash; 不需Developper认证的真机调试方法</strong> </p>
<p>以前，<a href="http://www.yifeiyang.net/introduction-to-iphone-development-10-devices-running-the-program/">iPhone开发入门（10）--- 设备上运行程序</a>中介绍了正式注册iPhone/iOS Developer Program后在设备运行程序的方法；在<a href="http://www.yifeiyang.net/iphone-development-introduction-3-linux-development-environment-on-the-build-iphone-os3-1-2/">iPhone开发入门（3）—Linux上构筑iPhone OS3.1.2开发环境</a> 和 <a href="http://www.yifeiyang.net/iphone-development-advanced-4-use-the-makefile-to-compile-iphone-program-automatically/">iPhone开发进阶（4） --- 使用Makefile自动编译iPhone程序</a> 也简单介绍了在越狱机器上运行程序的方法。</p>
<p>今天给大家介绍另一种不需要注册iPhone/iOS Developer Program，也可以简单地在设备中运行程序的方法.</p>
<h3><a name="sec1" id="sec1"></a>制作证明书</h3>
<dl>
<dt><strong>首先通过Keychain制作一个新的证明书 Keychain Access -&gt; Certificate Assistant -&gt; Create a Certification</strong></dt>
<dd></dd>
</dl>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/developper/1.png" rel="lightbox[436]"> <img src="http://www.yifeiyang.net/images/iphone/developper/1.png" alt="1" /></a> </p>
</div>
<p>证明书的名称任意(需要记住，下面会用到)，固有类型选择self-signed root，证明书类型选择Code Signing，并选择“let me override defaults”。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/developper/2.png" rel="lightbox[436]"> <img src="http://www.yifeiyang.net/images/iphone/developper/2.png" alt="2" /></a> </p>
</div>
<h3><a name="sec2" id="sec2"></a>程序设置</h3>
<p class="first">程序的Code Signing都设置为「Don’t Code Sign」</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/developper/6.png" rel="lightbox[436]"> <img src="http://www.yifeiyang.net/images/iphone/developper/6.png" alt="6" /></a> </p>
</div>
<dl>
<dt><strong>编辑SDKSettings.plist文件</strong></dt>
<dd>
<p>/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/SDKSettings.plist</p>
</dd>
</dl>
<p>将 CODE_SIGNING_REQUIRED 设置为 NO。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/developper/8.png" rel="lightbox[436]"> <img src="http://www.yifeiyang.net/images/iphone/developper/8.png" alt="8" /></a> </p>
</div>
<p>设置之后编译程序 for iOS Device。</p>
<h3><a name="sec3" id="sec3"></a>署名</h3>
<dl>
<dt><strong>在控制台用codesign给应用程序署名</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">codesign &#8211;fs &#8220;Fake Code Sign&#8221; ./Application.app/Application
</pre>
</td>
<tr></table>
</div>
<h3><a name="sec4" id="sec4"></a>验证</h3>
<p class="first">制作好Application.app之后，通过iPhoneExplorer，iFile等上传到iPhone的/Applications目录下。并添加执行属性。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/developper/17.png" rel="lightbox[436]"><br />
<img src="http://www.yifeiyang.net/images/iphone/developper/17.png" alt="17" /></a>
</p>
</div>
<p>OK, 可以运行一下我们的程序看看了。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/iphone-release-of-developing-skills-paper-6/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>敏捷开发实践（2） &#8212; 敏捷软件开发者的习惯</title>
		<link>http://www.yifeiyang.net/agile-development-practice-2/</link>
		<comments>http://www.yifeiyang.net/agile-development-practice-2/#comments</comments>
		<pubDate>Wed, 21 Dec 2011 01:42:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[敏捷开发]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/%e6%95%8f%e6%8d%b7%e5%bc%80%e5%8f%91%e5%ae%9e%e8%b7%b5%ef%bc%882%ef%bc%89-%e6%95%8f%e6%8d%b7%e8%bd%af%e4%bb%b6%e5%bc%80%e5%8f%91%e8%80%85%e7%9a%84%e4%b9%a0%e6%83%af/</guid>
		<description><![CDATA[<div class="contents">
<dt> <a href="#sec1">敏捷开发者的态度</a> </dt>
<dt> <a href="#sec2">敏捷开发者的习惯</a> </dt>
</div>
<p style="text-align: center;"> <strong>敏捷开发实践（2） &#8212; 敏捷软件开发者的习惯</strong> </p>
<p>敏捷开发的最小单位就是参与敏捷开发的个人。将这些敏捷开发者聚集起来，就形成了敏捷开发团队。</p>
<p>正如<a href="http://www.yifeiyang.net/agile-development-practices-1-about-agile-software-development/">上回</a>介绍的，敏捷开发是一种以人为核心、迭代、循序渐进的开发方法，它以最大可能地发挥团队的作用为[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/agile-development-practice-2/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt> <a href="#sec1">敏捷开发者的态度</a> </dt>
<dt> <a href="#sec2">敏捷开发者的习惯</a> </dt>
</p></div>
<p style="text-align: center;"> <strong>敏捷开发实践（2） &mdash; 敏捷软件开发者的习惯</strong> </p>
<p>敏捷开发的最小单位就是参与敏捷开发的个人。将这些敏捷开发者聚集起来，就形成了敏捷开发团队。</p>
<p>正如<a href="http://www.yifeiyang.net/agile-development-practices-1-about-agile-software-development/">上回</a>介绍的，敏捷开发是一种以人为核心、迭代、循序渐进的开发方法，它以最大可能地发挥团队的作用为目的。根据需要，随时改善，以降低软件开发中的风险。</p>
<h4><a name="sec1" id="sec1"></a>敏捷开发者的态度</h4>
<p class="first">敏捷开发者首先需要有忠实，勤恳的态度，在此之上要有持续改善和迅速达成目标的紧迫感。如何让开发者养成敏捷的心态，如何磨练开发者敏捷的意志，让开发者了解敏捷的习惯很重要。</p>
<p>习惯来自于经验，习惯需要用实践来养成。我们来看看作为敏捷软件开发者必备的4种技能 ：</p>
<ul>
<li>编故事（Creating Stories）
<p>这里不是让你去写一部小说，而是让开发者站在用户的视点，用用户能理解的词汇描述软件系统的机能，行为。只有理解了用户真正的需求，我们才能写出真正需要的软件。</p>
</li>
<li>制定计划（Planning）
<p>敏捷开发并不是没有计划就投入编码。指定计划也是很重要的，但是与以往的开发计划不同，敏捷的计划是随着开发的实际情况改变而随时制定并改善的。所以说敏捷开发的计划的作成、修改的频度很高，需要尽可能高效地完成每次制定的计划。</p>
</li>
<li>测试驱动开发（Test-Driven Development）
<p>它的基本思想就是在开发功能代码之前，先编写测试代码。也就是说在明确要开发某个功能后，首先思考如何对这个功能进行测试，并完成测试代码的编写，然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能，直到完成全部功能的开发。</p>
</li>
<li>重构（Refactoring）
<p>没有最好，只有更好。软件开发也不例外，任何时候我们都可以对软件代码做任何更动以增加可读性或者简化结构而不影响输出结果。</p>
</li>
</ul>
<h4><a name="sec2" id="sec2"></a>敏捷开发者的习惯</h4>
<p class="first">了解了开发者应该具有的态度，我们就可以从自己开始掌握敏捷开发者的习惯。然后在团队中展开，最终形成敏捷的团队。</p>
<dl>
<dt><strong>构成敏捷习惯的要素</strong></dt>
<dd>
<ul>
<li>敏捷的心态</li>
<li>敏捷的实践</li>
<li>持续改善</li>
</ul>
</dd>
</dl>
<h5>重视各种反馈</h5>
<p>通过实践中的反馈，我们可以得到过程中的经验，并对今后的开发产生有益的作用。但是并不是一味的重视实践就好了，需要知道何时从实践和反馈中学习。</p>
<p>为了按阶段，周期性的完成实践及反馈的过程，差生了迭代的概念。</p>
<h5>迭代开发</h5>
<p>迭代开发方法中，整个开发工作被组织为一系列的短小的、固定长度（如2周）的小项目，被称为一系列的迭代。每一次迭代都包括了需求分析、设计、实现与测试。采用这种方法，开发工作可以在需求被完整地确定之前启动，并在一次迭代中完成系统的一部分功能或业务逻辑的开发工作。再通过客户的反馈来细化需求，并开始新一轮的迭代。</p>
<p>迭代的一个很重要思想就是将作业按时间单位来划分，管理。在一定的周期内完成开发的作业。不同的团队可以按照机能划分，并行开展，如下图 ：</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/agile/iterative.jpg" rel="lightbox[435]"> <img src="http://www.yifeiyang.net/images/agile/iterative.jpg" alt="iterative" /></a> </p>
</div>
<p>比如，一个项目的作业期间是3个月，按2周为单位来开展迭代开发，那么迭代的总数就是6个。并且该期限是严守的，一旦规定好了一般是不能更改的。</p>
<h5>回顾改善</h5>
<p>每个迭代结束之后，都需要回顾上个迭代中的内容，考虑是否有改善和坚持的地方，以提高接下来迭代中的开发效率。</p>
<p>该回顾需要定期的地实施，并在30分钟到1个小时内完成。一般情况下，在白板上使用KPT方法来总结课题。</p>
<ul>
<li>Keep : 好的，需要今后坚持的</li>
<li>Problem : 问题点，需要注意并改善的</li>
<li>Try : 下次开始尝试改善的</li>
</ul>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/agile/kpt.jpg" rel="lightbox[435]"> <img src="http://www.yifeiyang.net/images/agile/kpt.jpg" alt="kpt" /></a> </p>
</div>
<dl>
<dt><strong>进行KPT的步骤</strong></dt>
<dd></dd>
</dl>
<ul>
<li>检查上次的Try课题</li>
<li>回忆本次迭代中遇到的课题</li>
<li>按Keep，Problem，Try的形式总结</li>
<li>Closing，总结，给白板拍照</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/agile-development-practice-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iPhone开发技巧之环境篇（11） &#8212;  让Xcode对应多个版本的iOS SDK</title>
		<link>http://www.yifeiyang.net/the-environment-of-developing-skills-iphone-paper-11/</link>
		<comments>http://www.yifeiyang.net/the-environment-of-developing-skills-iphone-paper-11/#comments</comments>
		<pubDate>Sat, 03 Dec 2011 00:52:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[开发技巧]]></category>
		<category><![CDATA[环境篇]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/iphone%e5%bc%80%e5%8f%91%e6%8a%80%e5%b7%a7%e4%b9%8b%e7%8e%af%e5%a2%83%e7%af%87%ef%bc%8811%ef%bc%89-%e8%ae%a9xcode%e5%af%b9%e5%ba%94%e5%a4%9a%e4%b8%aa%e7%89%88%e6%9c%ac%e7%9a%84ios-sdk/</guid>
		<description><![CDATA[<div class="contents">
<dt> <a href="#sec1">安装新版Xcode时备份旧版SDK</a> </dt>
<dt> <a href="#sec2">在Xcode内安装旧版的SDK</a> </dt>
<dt> <a href="#sec3">由于编译SDK版本不同而引起的编译/运行期错误处理</a> </dt>
</div>
<p style="text-align: center;"> <strong>iPhone开发技巧之环境篇（11）&#8212; 让Xcode对应多个版本的iOS SDK</strong> </p>
<p>每次Xcode升级，以前的iOS SDK都会被覆盖掉；这是许多人碰[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/the-environment-of-developing-skills-iphone-paper-11/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt> <a href="#sec1">安装新版Xcode时备份旧版SDK</a> </dt>
<dt> <a href="#sec2">在Xcode内安装旧版的SDK</a> </dt>
<dt> <a href="#sec3">由于编译SDK版本不同而引起的编译/运行期错误处理</a> </dt>
</p></div>
<p style="text-align: center;"> <strong>iPhone开发技巧之环境篇（11）&mdash; 让Xcode对应多个版本的iOS SDK</strong> </p>
<p>每次Xcode升级，以前的iOS SDK都会被覆盖掉；这是许多人碰到并头疼的问题&mdash;-如果想用旧版本的SDK编译就没有办法了。</p>
<p>这里就介绍一下Xcode与多个SDK版本并存的方法。针对Xcode的安装顺序总结了两种方法 ：</p>
<h4><a name="sec1" id="sec1"></a>安装新版Xcode时备份旧版SDK</h4>
<p class="first">这是最简单的方法 &mdash;- 每当安装新版的Xcode时，备份之前旧的SDK，安装Xcode之后，回复到指定目录下即可。</p>
<dl>
<dt><strong>备份SDK路径</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">iOS &#35774;&#22791;&#29992;
$ /Developer/Platforms/iPhoneOS.platform/Developer/SDKs

iPhone &#27169;&#25311;&#22120;&#29992;
$ /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs
</pre>
</td>
<tr></table>
</div>
<p>简单起见，在安装新版Xcode之前，可以把这两个目录下的需要备份的SDK版本move到其他目录下，安装完Xcode之后再move回来即可。</p>
<h4><a name="sec2" id="sec2"></a>在Xcode内安装旧版的SDK</h4>
<p class="first">已经安装好的Xcode上，想安装旧版的SDK也是可以的，只是需要一些准备。下面从安装新版Xcode开始，再到之后安装旧版SDK做一个简要说明。</p>
<dl>
<dt><strong>首先我们删除之前已安装的Xcode</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">$ sudo /Developer/Library/uninstall-devtools --mode=all
</pre>
</td>
<tr></table>
</div>
<p>然后，安装从Apple Developer上下载的最新Xcode dmg安装包。完成Xcode的安装。</p>
<p>接下来，从<a href="http://iphonesdkdev.blogspot.com/2010/04/old-versions-of-iphone-sdk.html">iPhone Software Development: Old versions of iPhone SDK</a>上下载你所需要的旧版SDK版本；</p>
<p>打开下载的旧版dmg文件的Packages目录后，会看到许多的pkg文件。</p>
<p></br>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/sdk/sdk1.png" rel="lightbox[434]"><br />
<img src="http://www.yifeiyang.net/images/iphone/sdk/sdk1.png" alt="sdk" /></a>
</p>
</div>
<p>接下来，根据你的需要，选择具体的iPhoneSDK$(ver).pkg文件，并双击安装。这里注意一点 &mdash;- 你选择的SDK版本最好是dmg文件中正式附带的版本；在某个dmg会附带当前最新的版本，和之前版本的pkg文件。如果是选择之前pkg文件的话，有可能缺省的安装路径是不一样的。<br />
比如xcode_3.2.4_and_ios_sdk_4.1.dmg，就选择iPhoneSDK4_1.pkg来安装，不要选择比它小的版本号。</p>
<dl>
<dt><strong>安装的时候，需要需要选择正确的安装路径</strong></dt>
<dd></dd>
</dl>
<p>选择[Installation Type]的[Change Install Location...]</p>
<p></br>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/sdk/sdk2.png" rel="lightbox[434]"><br />
<img src="http://www.yifeiyang.net/images/iphone/sdk/sdk2.png" alt="sdk" /></a>
</p>
</div>
<p>然后选择[Developer]目录。</p>
<p></br>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/sdk/sdk3.png" rel="lightbox[434]"><br />
<img src="http://www.yifeiyang.net/images/iphone/sdk/sdk3.png" alt="sdk" /></a>
</p>
</div>
<p>确认好后，点击[Continue]。</p>
<p></br>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/sdk/sdk4.png" rel="lightbox[434]"><br />
<img src="http://www.yifeiyang.net/images/iphone/sdk/sdk4.png" alt="sdk" /></a>
</p>
</div>
<p>安装完后，我们可以确认一下下面的路径是否有你需要的SDK。</p>
<blockquote>
<p class="quoted">/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(ver).sdk</p>
</blockquote>
<p>同样的，你可以安装iPhoneSimulatorSDK$(ver).pkg</p>
<hr />
<p>虽然说Xcode可以支持多种版本的SDK并存，但是一般情况下按照下面的原则去编译你的程序应该是是最安全的。</p>
<blockquote>
<p class="quoted"><strong>用最新的Base SDK编译，Deployment Target尽量选择最低的。</strong></p>
</blockquote>
<hr />
<h4><a name="sec3" id="sec3"></a>由于编译SDK版本不同而引起的编译/运行期错误处理</h4>
<p class="first">由于SDK版本变化，或多或少会有一些莫名的编译错误出现；有的时候是链接出错，譬如找出到-lcrt，有的时候是压根编译时就找不到头文件。</p>
<dl>
<dt><strong>譬如下面找不到-lcrt的问题</strong></dt>
<dd></dd>
</dl>
<p></br>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/sdk/sdk5.png" rel="lightbox[434]"><br />
<img src="http://www.yifeiyang.net/images/iphone/sdk/sdk5.png" alt="sdk" /></a>
</p>
</div>
<p>这个时候，我们可以试试更改编译选项中Mac OS X的Deployment Target。</p>
<p></br>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/sdk/sdk6.png" rel="lightbox[434]"><br />
<img src="http://www.yifeiyang.net/images/iphone/sdk/sdk6.png" alt="sdk" /></a>
</p>
</div>
<p>如果还是不能解决，可以试试更改编译器（比如GCC 4.2 或者是 LLVM GCC 4.2）。</p>
<dl>
<dt><strong>还有一种问题的原因可能是原先SDK中的Framework与新版中的SDK不匹配，引起编译失败</strong></dt>
<dd></dd>
</dl>
<p>这个时候，需要重新选择新版本中的Framework到工程文件中。</p>
<dl>
<dt><strong>如果是旧的iOS环境中没有包含的必要的Framework，也会出现类似于下面的动态链接错误</strong></dt>
<dd>
（即在高版本的OS中才有的新增功能，想在低版本的OS中依然能够运行）</dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">dyld: Library not loaded: /System/Library/Frameworks/iAd.framework/iAd
  Referenced from: /var/mobile/Applications/71E4CD8F-4E9E-4FC1-A7B9-6AE975E9C2B0/Test.app/Test
  Reason: image not found
</pre>
</td>
<tr></table>
</div>
<p>这时，就需要与新版SDK的Framework建立Weak Link。</p>
<p>如下图所示，在「Target」＞「执行文件」点击左边的三角，并选择「链接库文件」。只要将需要Weak Link的库的Required变为 Weak 即可。</p>
<p></br>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/sdk/sdk7.png" rel="lightbox[434]"><br />
<img src="http://www.yifeiyang.net/images/iphone/sdk/sdk7.png" alt="sdk" /></a>
</p>
</div>
<p>最后，针对对新库中调用的部分，我们还需要在代码中加入对版本判断和控制的逻辑。（如果不这样做，我们的程序在低版本OS的设备中必定崩溃）</p>
<dl>
<dt><strong>1. iOS版本确认</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">- (<span class="type">void</span>)<span class="function-name">viewDidLoad</span> {
    [<span class="keyword">super</span> viewDidLoad];

    <span class="keyword">if</span> ([[[<span class="type">UIDevice</span> currentDevice] systemVersion] floatValue] &gt;= 4.0) {
        bannerView = [[<span class="type">ADBannerView</span> alloc] init];
        bannerView.delegate = <span class="keyword">self</span>;
        [<span class="keyword">self</span>.view addSubview:bannerView];
    }
</pre>
</td>
<tr></table>
</div>
<dl>
<dt><strong>2. 新类是否存在</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="type">Class</span> <span class="variable-name">clazz</span> = NSClassFromString(@<span class="string">"ADBannerView"</span>);
<span class="keyword">if</span> (clazz) {
    bannerView = [[<span class="type">ADBannerView</span> alloc] init];
    bannerView.delegate = <span class="keyword">self</span>;
    [<span class="keyword">self</span>.view addSubview:bannerView];
}
</pre>
</td>
<tr></table>
</div>
<dl>
<dt><strong>3. 新接口是否存在</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="keyword">if</span> (UIGraphicsBeginImageContextWithOptions != NULL) {
    bannerView = [[<span class="type">ADBannerView</span> alloc] init];
    bannerView.delegate = <span class="keyword">self</span>;
    [<span class="keyword">self</span>.view addSubview:bannerView];
}
</pre>
</td>
<tr></table>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/the-environment-of-developing-skills-iphone-paper-11/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>iPhone开发技巧之发布篇（5）&#8212; 在程序中添加广告</title>
		<link>http://www.yifeiyang.net/iphone-development-skills-of-the-articles-published-5-add-an-ad-in-the-program/</link>
		<comments>http://www.yifeiyang.net/iphone-development-skills-of-the-articles-published-5-add-an-ad-in-the-program/#comments</comments>
		<pubDate>Sun, 20 Nov 2011 00:57:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[发布篇]]></category>
		<category><![CDATA[开发技巧]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[iPad]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/iphone%e5%bc%80%e5%8f%91%e6%8a%80%e5%b7%a7%e4%b9%8b%e5%8f%91%e5%b8%83%e7%af%87%ef%bc%885%ef%bc%89-%e5%9c%a8%e7%a8%8b%e5%ba%8f%e4%b8%ad%e6%b7%bb%e5%8a%a0%e5%b9%bf%e5%91%8a/</guid>
		<description><![CDATA[<div class="contents">
<dt> <a href="#sec1">iAd</a> </dt>
<dd>
<dl>
<dt> <a href="#sec2">追加iAD Framework</a> </dt>
<dt> <a href="#sec3">创建ADBannerView</a> </dt>
</dl>
</dd>
<dt> <a href="#sec4">AdMob</a> </dt>
<dd>
<dl>
<dt> <a href="#sec5">登陆</a> </dt>
<dt> <a href="#sec6">注意点</a> </dt>
<dt> <a href="#sec7">例程</a> </dt>
</dl>
</dd></div>
<p style="text-align: center;"> <strong>iPhone开发技巧之发布篇（5）&#8212; 在程序中添加广告</strong> </p>
<p>iPhone/iPad的程序，即使是Free的版本，也可以通过广告给我们带来收入[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/iphone-development-skills-of-the-articles-published-5-add-an-ad-in-the-program/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt> <a href="#sec1">iAd</a> </dt>
<dd>
<dl>
<dt> <a href="#sec2">追加iAD Framework</a> </dt>
<dt> <a href="#sec3">创建ADBannerView</a> </dt>
</dl>
</dd>
<dt> <a href="#sec4">AdMob</a> </dt>
<dd>
<dl>
<dt> <a href="#sec5">登陆</a> </dt>
<dt> <a href="#sec6">注意点</a> </dt>
<dt> <a href="#sec7">例程</a> </dt>
</dl>
</dd></div>
<p style="text-align: center;"> <strong>iPhone开发技巧之发布篇（5）&mdash; 在程序中添加广告</strong> </p>
<p>iPhone/iPad的程序，即使是Free的版本，也可以通过广告给我们带来收入。前提是你的程序足够吸引人，有足够的下载量。</p>
<p>这里，我将介绍一下程序中集成广告的方法。主要有两种广告<a href="http://developer.apple.com/iAD/">iAd</a>和<a href="http://www.admob.com">AdMob</a>。（还有其他多种可被植入的广告SDK，这里就不都一一介绍了）</p>
<h3><a name="sec1" id="sec1"></a><a href="http://developer.apple.com/iAD/">iAd</a></h3>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/iad.png" rel="lightbox[433]"> <img src="http://www.yifeiyang.net/images/iphone/iad.png" alt="iad" /></a> </p>
</div>
<p>从iOS 4开始，Apple增加了叫做 <strong>iAd</strong> 的架构，通过它我们可以在程序中提供Apple的广告服务。Apple会支付给开发者60%的广告收入。</p>
<p><a href="http://developer.apple.com/library/ios/navigation/#section=Frameworks&amp;topic=iAd">iAd Framework</a>中有例程，我们可以下载学习。这里，把简单的步骤说明一下 ：</p>
<h4><a name="sec2" id="sec2"></a>追加iAD Framework</h4>
<p class="first">首先，在Xcode的［Frameworks］中添加［iAd.framework］。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/iad3.jpg" rel="lightbox[433]"> <img src="http://www.yifeiyang.net/images/iphone/iad3.jpg" alt="iad" /></a> </p>
</div>
<h4><a name="sec3" id="sec3"></a>创建ADBannerView</h4>
<p class="first">广告的显示是在一个叫做ADBannerView的窗口中的。通过控制这个窗口可以控制广告的显示/隐藏。ADBannerView和一般的UIView没有什么两样，将其作为某个画面的subView，然后通过「frame」控制其显示的位置，大小。一般情况下，缺省iPhone上的话，竖屏是：横320pt, 竖50pt；横屏是：横480pt, 竖32pt。</p>
<p>下面的程序显示了ADBannerView的初始化过程，以父窗口的viewDidLoad中实现为例。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">- (<span class="type">void</span>)<span class="function-name">viewDidLoad</span> {

&#8230;&#8230;&#12304;&#30465;&#30053;&#12305;&#8230;&#8230;

    <span class="comment-delimiter">// </span><span class="comment">&#21021;&#22987;&#21270;ADBannerView
</span>    ADBannerView *adView = [[[<span class="type">ADBannerView</span> alloc] initWithFrame:CGRectZero] autorelease];
    adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
    <span class="comment-delimiter">// </span><span class="comment">&#30331;&#38470;ADBannerView&#30340;delegate&#65292;&#36825;&#37324;&#25105;&#20204;&#35774;&#23450;&#20854;&#29238;&#31383;&#21475;&#33258;&#24049;
</span>    adView.delegate = <span class="keyword">self</span>;
    <span class="comment-delimiter">// </span><span class="comment">&#22312;&#29238;&#31383;&#21475;&#19979;&#26041;&#34920;&#31034;
</span>    adView.frame = CGRectOffset(adView.frame, 0, <span class="keyword">self</span>.view.frame.size.height - <span class="keyword">self</span>.adView.frame.size.height);
    <span class="comment-delimiter">// </span><span class="comment">&#28155;&#21152;&#21040;&#29238;&#31383;&#21475;&#20013;
</span>    [<span class="keyword">self</span>.view addSubview:adView];
}
</pre>
</td>
<tr></table>
</div>
<p>接下来，我们来实现ADBannerView的delegate。这里可以实现在父窗口的UIViewController子类中，也可以单独写一个ViewController。这里面实现了ADBannerView广告的读取，错误处理，全画面表示等delegate的处理设定。</p>
<p>下面实现在父窗口的ViewController中。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="preprocessor">#import</span> <span class="string">&lt;UIKit/UIKit.h&gt;</span>
<span class="preprocessor">#import</span> <span class="string">&lt;iAd/iAd.h&gt;</span>

<span class="keyword">@interface</span> <span class="type">XXXViewController</span> : <span class="type">UIViewController</span>&lt;<span class="type">UITextFieldDelegate</span>, <span class="type">ADBannerViewDelegate</span>&gt; {

&#8230;&#8230;&#12304;&#30465;&#30053;&#12305;&#8230;&#8230;
}
</pre>
</td>
<tr></table>
</div>
<p>如上所示，这里增加了「ADBannerViewDelegate」protocol的实现。接下来看看都有哪些delegate接口。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="comment-delimiter">// </span><span class="comment">&#24191;&#21578;&#35835;&#21462;&#36807;&#31243;&#20013;&#20986;&#29616;&#38169;&#35823;
</span>- (<span class="type">void</span>)<span class="function-name">bannerView</span>:(<span class="type">ADBannerView</span> *)<span class="variable-name">banner</span> <span class="function-name">didFailToReceiveAdWithError</span>:(<span class="type">NSError</span> * )<span class="variable-name">error</span>{
    <span class="comment-delimiter">// </span><span class="comment">&#20999;&#25442;ADBannerView&#34920;&#31034;&#29366;&#24577;&#65292;&#26174;&#31034;&#8594;&#38544;&#34255;
</span>    <span class="comment-delimiter">// </span><span class="comment">adView.frame = CGRectOffset(adView.frame, 0, self.view.frame.size.height);
</span>}

<span class="comment-delimiter">// </span><span class="comment">&#25104;&#21151;&#35835;&#21462;&#24191;&#21578;
</span>- (<span class="type">void</span>)<span class="function-name">bannerViewDidLoadAd</span>:(<span class="type">ADBannerView</span> *)<span class="variable-name">banner</span>{
    <span class="comment-delimiter">// </span><span class="comment">&#20999;&#25442;ADBannerView&#34920;&#31034;&#29366;&#24577;&#65292;&#38544;&#34255;&#8594;&#26174;&#31034;
</span>    <span class="comment-delimiter">// </span><span class="comment">adView.frame = CGRectOffset(adView.frame, 0, self.view.frame.size.height - adView.frame.size.height);
</span>}

<span class="comment-delimiter">// </span><span class="comment">&#29992;&#25143;&#28857;&#20987;&#24191;&#21578;&#26159;&#21709;&#24212;&#65292;&#36820;&#22238;&#20540;BOOL&#25351;&#23450;&#24191;&#21578;&#26159;&#21542;&#25171;&#24320;
</span><span class="comment-delimiter">// </span><span class="comment">&#21442;&#25968;willLeaveApplication&#26159;&#25351;&#26159;&#21542;&#29992;&#20854;&#20182;&#30340;&#31243;&#24207;&#25171;&#24320;&#35813;&#24191;&#21578;
</span><span class="comment-delimiter">// </span><span class="comment">&#19968;&#33324;&#22312;&#35813;&#20989;&#25968;&#20869;&#35753;&#24403;&#21069;View&#20572;&#27490;&#65292;&#20197;&#21450;&#20934;&#22791;&#20840;&#30011;&#38754;&#34920;&#31034;&#24191;&#21578;
</span>- (<span class="type">BOOL</span>)<span class="function-name">bannerViewActionShouldBegin</span>:(<span class="type">ADBannerView</span> *)<span class="variable-name">banner</span> <span class="function-name">willLeaveApplication</span>:(<span class="type">BOOL</span>)<span class="variable-name">willLeave</span> {
    NSLog(@<span class="string">"bannerViewActionShouldBegin:willLeaveApplication: is called."</span>);
}

<span class="comment-delimiter">// </span><span class="comment">&#20840;&#30011;&#38754;&#30340;&#24191;&#21578;&#34920;&#31034;&#23436;&#20102;&#21518;&#65292;&#35843;&#29992;&#35813;&#25509;&#21475;
</span><span class="comment-delimiter">// </span><span class="comment">&#35813;&#25509;&#21475;&#34987;&#35843;&#29992;&#20043;&#21518;&#65292;&#24403;&#21069;&#31243;&#24207;&#19968;&#33324;&#20250;&#20316;&#20026;&#21518;&#21488;&#31243;&#24207;&#36816;&#34892;
</span><span class="comment-delimiter">// </span><span class="comment">&#35813;&#25509;&#21475;&#20013;&#38656;&#35201;&#22238;&#22797;&#20043;&#21069;&#34987;&#20013;&#26029;&#30340;&#22788;&#29702;&#65288;&#22914;&#26524;&#26377;&#30340;&#35805;&#65289;
</span>- (<span class="type">void</span>)<span class="function-name">bannerViewActionDidFinish</span>:(<span class="type">ADBannerView</span> *)<span class="variable-name">banner</span> {
    NSLog(@<span class="string">"bannerViewActionDidFinish: is called."</span>);
}
</pre>
</td>
<tr></table>
</div>
<p>以上都实现之后，我们来看看iAd广告的效果。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/iad2.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/iad2.png" alt="iad" /></a>
</p>
</div>
<h3><a name="sec4" id="sec4"></a><a href="http://www.admob.com">AdMob</a></h3>
<p class="first">AdMob是另一家移动电话广告市场，现在已被Google收购。借助AdMob, 谷歌计划将其网络搜索主导地位从电脑平台扩展向手机平台。</p>
<p>正因为AdMob现在是Google的了，所以在Android上得到广泛的应用，不过在iPhone上也是同样可以使用的。下面就介绍一下使用方法。</p>
<p>其实步骤很简单：在<a href="http://www.admob.com">AdMob</a>上注册用户→登陆你的程序→得到程序固有的Publisher ID→下载并在程序中组入相应代码。</p>
<h4><a name="sec5" id="sec5"></a>登陆</h4>
<dl>
<dt><strong>首先注册用户</strong></dt>
<dd></dd>
</dl>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob1.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob1.png" alt="Admob" /></a>
</p>
</div>
<p>注册并登陆之后，需要登记你准备添加广告的程序（这里，即使程序还没有开发完毕也没有关系）。点击下图marketplace→添加site/Application</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob2.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob2.png" alt="Admob" /></a>
</p>
</div>
<p>选择iPhone/iPad程序，如果是Android的话就选择Android应用程序。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob3.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob3.png" alt="Admob" /></a>
</p>
</div>
<p>接下来填写程序的详细信息。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob4.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob4.png" alt="Admob" /></a>
</p>
</div>
<p>其中关于程序的URL的填写，如果程序已经发布，那么填写App Store上的URL，否则随便先填一个，以后可以修改。</p>
<p>接下来就可以得到程序的Publisher ID，其使用方法会在下面介绍。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob5.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob5.png" alt="Admob" /></a>
</p>
</div>
<p>同时，我们可以下载最新的SDK。（其实也可以通过<a href="http://code.google.com/intl/ja-JP/mobile/ads/download.html#downloadios">这里</a>下载）</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob6.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob6.png" alt="Admob" /></a>
</p>
</div>
<p>下载并解压之后，会得到如下图的几个文件。其中README.txt有环境说明，<a href="http://code.google.com/intl/ja/mobile/ads/docs/ios/fundamentals.html">文档</a>及<a href="http://dl.google.com/googleadmobadssdk/examples/ios-fundamentals.zip">例程</a>的下载URL。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob7.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob7.png" alt="Admob" /></a>
</p>
</div>
<h4><a name="sec6" id="sec6"></a>注意点</h4>
<p class="first">接下来我们看看程序中怎么使用该SDK。首先我们看看例程中有哪些需要注意的。</p>
<dl>
<dt><strong>MY BANNER_UNIT_ID的设定</strong></dt>
<dd></dd>
</dl>
<p>例程中有下面这样的定义（BannerExampleViewController.m）。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="preprocessor">#if</span> <span class="negation-char">!</span><span class="preprocessor">defined</span>(MY_BANNER_UNIT_ID)
<span class="preprocessor">  #error</span> <span class="string">"You must define MY_BANNER_UNIT_ID as your AdMob Publisher ID"</span>
<span class="preprocessor">#endif</span>
</pre>
</td>
<tr></table>
</div>
<p>这里就需要上面介绍的注册时得到的程序专有的Publisher ID。没有它，编译的时候将报错。类似下面的样子，我们设定一下。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="preprocessor">#define</span> <span class="variable-name">MY_BANNER_UNIT_ID</span>   @<span class="string">"xxxxxxxxxxxxxxx"</span>

<span class="preprocessor">#if</span> <span class="negation-char">!</span><span class="preprocessor">defined</span>(MY_BANNER_UNIT_ID)
<span class="preprocessor">  #error</span> <span class="string">"You must define MY_BANNER_UNIT_ID as your AdMob Publisher ID"</span>
<span class="preprocessor">#endif</span>
</pre>
</td>
<tr></table>
</div>
<dl>
<dt><strong>另外，还有一个测试时用的属性</strong></dt>
<dd></dd>
</dl>
<p>测试的时候将 GADRequest::testing 属性置为 YES。如下For Testing的设置。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">  <span class="comment-delimiter">// </span><span class="comment">Let the runtime know which UIViewController to restore after taking
</span>  <span class="comment-delimiter">// </span><span class="comment">the user wherever the ad goes and add it to the view hierarchy.
</span>  bannerView_.rootViewController = <span class="keyword">self</span>;
  [<span class="keyword">self</span>.view addSubview:bannerView_];

  <span class="comment-delimiter">// </span><span class="comment">For Testing
</span>  <span class="type">GADRequest</span> *<span class="variable-name">rq</span> = [<span class="type">GADRequest</span> request];
  rq.testing = <span class="constant">YES</span>;

  <span class="comment-delimiter">// </span><span class="comment">Initiate a generic request to load it with an ad.
</span>  [bannerView_ loadRequest:rq];
</pre>
</td>
<tr></table>
</div>
<p>显示的广告如下图。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob8.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob8.png" alt="Admob" /></a>
</p>
</div>
<p>不过，该设定只对模拟器有效，在实际设备上运行时，仍然显示真实的广告。如下图。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob9.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob9.png" alt="Admob" /></a>
</p>
</div>
<h4><a name="sec7" id="sec7"></a>例程</h4>
<p class="first">接下来，通过一个例程说明一下AdMob广告的添加过程。</p>
<p>1. Xcode 4中创建一个「Tab Bar Application」新程序「AdMobTabBar」</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob10.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob10.png" alt="Admob" /></a>
</p>
</div>
<p>2. 将 AdMob SDK 放到该工程中。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob11.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob11.png" alt="Admob" /></a>
</p>
</div>
<p>3. 添加 AdMob 所必须的 Framework（ AudioToolbox, MediaPlayer, MessageUI, SystemConfiguration。）</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob12.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob12.png" alt="Admob" /></a>
</p>
</div>
<p>4. 创建GADBannerView</p>
<p>与iAD中的ADBannerView类似，AdMob也有一个GADBannerView，用来显示广告。其创建过程如下。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;">    <span class="comment-delimiter">// </span><span class="comment">Create a view of the standard size at the bottom of the screen.
</span>    bannerView_ = [[<span class="type">GADBannerView</span> alloc]
                            initWithFrame:CGRectMake(0.0,
                                            <span class="keyword">self</span>.view.frame.size.height -
                                            TABBAR_HEIGHT,
                                            GAD_SIZE_320x50.width,
                                            GAD_SIZE_320x50.height)];

    <span class="comment-delimiter">// </span><span class="comment">delegate &#12398;&#35373;&#23450;
</span>    bannerView_.delegate = <span class="keyword">self</span>;

    <span class="comment-delimiter">// </span><span class="comment">Specify the ad's "unit identifier." This is your AdMob Publisher ID.
</span>    bannerView_.adUnitID = MY_BANNER_UNIT_ID;

    <span class="comment-delimiter">// </span><span class="comment">Let the runtime know which UIViewController to restore after taking
</span>    <span class="comment-delimiter">// </span><span class="comment">the user wherever the ad goes and add it to the view hierarchy.
</span>    bannerView_.rootViewController = <span class="keyword">self</span>;
    [<span class="keyword">self</span>.view addSubview:bannerView_];

    <span class="comment-delimiter">// </span><span class="comment">For Testing
</span>    <span class="type">GADRequest</span> *<span class="variable-name">rq</span> = [<span class="type">GADRequest</span> request];
    rq.testing = <span class="constant">YES</span>;

    <span class="comment-delimiter">// </span><span class="comment">Initiate a generic request to load it with an ad.
</span>    [bannerView_ loadRequest:rq];
</pre>
</td>
<tr></table>
</div>
<p>同样，也有一个叫做GADBannerViewDelegate的delegate。可以实现在父窗口的UIViewController子类中，也可以单独写一个ViewController。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="preprocessor">#import</span> <span class="string">&lt;UIKit/UIKit.h&gt;</span>

<span class="preprocessor">#import</span> <span class="string">"GADBannerView.h"</span>

<span class="keyword">@interface</span> <span class="type">FirstViewController</span> : <span class="type">UIViewController</span> &lt;<span class="type">GADBannerViewDelegate</span>&gt; {
    <span class="type">GADBannerView</span> *<span class="variable-name">bannerView_</span>;

}

<span class="keyword">@end</span>
</pre>
</td>
<tr></table>
</div>
<dl>
<dt><strong>或者</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="preprocessor">#import</span> <span class="string">"GADBannerView.h"</span>
<span class="preprocessor">#import</span> <span class="string">"GADBannerViewDelegate.h"</span>

<span class="keyword">@interface</span> <span class="type">MyBannerView</span> : <span class="type">GADBannerView</span> &lt;<span class="type">GADBannerViewDelegate</span>&gt; {
}

<span class="keyword">@end</span>
</pre>
</td>
<tr></table>
</div>
<p>这里的delegate接口与ADBannerViewDelegate其实很类似。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="comment-delimiter">// </span><span class="comment">Admob&#25104;&#21151;&#21462;&#24471;
</span>- (<span class="type">void</span>)<span class="function-name">adViewDidReceiveAd</span>:(<span class="type">GADBannerView</span> *)<span class="variable-name">adMobView</span> {
    NSLog(@<span class="string">"Admob:adViewDidReceiveAd"</span>);

    <span class="comment-delimiter">// </span><span class="comment">&#21160;&#30011;&#34920;&#31034;
</span>    adMobAd.frame = CGRectMake(0.0,
             <span class="keyword">self</span>.view.frame.size.height,
             adMobView.frame.size.width,
             adMobView.frame.size.height);
    [<span class="type">UIView</span> beginAnimations:@<span class="string">"AdMobBannerMoveOnScreen"</span> context:NULL];
    adMobAd.frame = CGRectMake(0.0,
             <span class="keyword">self</span>.view.frame.size.height - adMobView.frame.size.height,
             adMobView.frame.size.width,
             adMobView.frame.size.height);
    [<span class="type">UIView</span> commitAnimations];
}

<span class="comment-delimiter">// </span><span class="comment">Admob&#21462;&#24471;&#22833;&#36133;
</span>- (<span class="type">void</span>)<span class="function-name">adView</span>:(<span class="type">GADBannerView</span> *)<span class="variable-name">adMobView</span> <span class="function-name">didFailToReceiveAdWithError</span>:(<span class="type">GADRequestError</span> *)<span class="variable-name">error</span> {
    NSLog(@<span class="string">"Admob:didFailToReceiveAdWithError:%@"</span>, [error localizedDescription]);
}

<span class="comment-delimiter">// </span><span class="comment">AdMob&#24191;&#21578;&#34987;&#25171;&#24320;&#26102;
</span>- (<span class="type">void</span>)<span class="function-name">adViewWillPresentScreen</span>:(<span class="type">GADBannerView</span> *)<span class="variable-name">adView</span> {
}

<span class="comment-delimiter">// </span><span class="comment">&#20174;AdMob&#30340;&#24191;&#21578;&#36339;&#21040;&#20854;&#20182;&#31243;&#24207;&#26102;
</span>-(<span class="type">void</span>) <span class="function-name">applicationDidEnterBackground</span>:(<span class="type">UIApplication</span>*)<span class="variable-name">application</span> {
}

<span class="comment-delimiter">// </span><span class="comment">&#20174;&#20854;&#20182;&#34987;&#25171;&#24320;&#30340;&#31243;&#24207;&#36820;&#22238;&#21040;AdMob&#24191;&#21578;&#26174;&#31034;
</span>-(<span class="type">void</span>) <span class="function-name">applicationWillEnterForeground</span>:(<span class="type">UIApplication</span>*)<span class="variable-name">application</span> {
}

<span class="comment-delimiter">// </span><span class="comment">AdMob&#24191;&#21578;&#26174;&#31034;&#34987;&#20851;&#38381;&#26102;
</span>- (<span class="type">void</span>)<span class="function-name">adViewWillDismissScreen</span>:(<span class="type">GADBannerView</span> *)<span class="variable-name">adView</span> {
}
</pre>
</td>
<tr></table>
</div>
<p>整体的代码如下所示 。这里GADBannerView的delegate实现在父窗口的ViewController中。</p>
<div class="codebox">
<table width="100%">
<tr>
<td class="line_numbers">
<pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"><span class="preprocessor">#import</span> <span class="string">"FirstViewController.h"</span>

<span class="preprocessor">#define</span> <span class="variable-name">MY_BANNER_UNIT_ID</span>   @<span class="string">"xxxxxxxxxxxxxxx"</span>
<span class="preprocessor">#define</span> <span class="variable-name">ANIMATION_DURATION</span>  0.5f
<span class="preprocessor">#define</span> <span class="variable-name">TABBAR_HEIGHT</span>       49.0f

<span class="keyword">@implementation</span> <span class="type">FirstViewController</span>

<span class="comment-delimiter">// </span><span class="comment">Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
</span>- (<span class="type">void</span>)<span class="function-name">viewDidLoad</span>
{
    [<span class="keyword">super</span> viewDidLoad];

    <span class="comment-delimiter">// </span><span class="comment">Create a view of the standard size at the bottom of the screen.
</span>    bannerView_ = [[<span class="type">GADBannerView</span> alloc]
                            initWithFrame:CGRectMake(0.0,
                                            <span class="keyword">self</span>.view.frame.size.height -
                                            TABBAR_HEIGHT,
                                            GAD_SIZE_320x50.width,
                                            GAD_SIZE_320x50.height)];

    <span class="comment-delimiter">// </span><span class="comment">delegate &#12398;&#35373;&#23450;
</span>    bannerView_.delegate = <span class="keyword">self</span>;

    <span class="comment-delimiter">// </span><span class="comment">Specify the ad's "unit identifier." This is your AdMob Publisher ID.
</span>    bannerView_.adUnitID = MY_BANNER_UNIT_ID;

    <span class="comment-delimiter">// </span><span class="comment">Let the runtime know which UIViewController to restore after taking
</span>    <span class="comment-delimiter">// </span><span class="comment">the user wherever the ad goes and add it to the view hierarchy.
</span>    bannerView_.rootViewController = <span class="keyword">self</span>;
    [<span class="keyword">self</span>.view addSubview:bannerView_];

    <span class="comment-delimiter">// </span><span class="comment">For Testing
</span>    <span class="type">GADRequest</span> *<span class="variable-name">rq</span> = [<span class="type">GADRequest</span> request];
    rq.testing = <span class="constant">YES</span>;

    <span class="comment-delimiter">// </span><span class="comment">Initiate a generic request to load it with an ad.
</span>    [bannerView_ loadRequest:rq];
}

- (<span class="type">BOOL</span>)<span class="function-name">shouldAutorotateToInterfaceOrientation</span>:(<span class="type">UIInterfaceOrientation</span>)<span class="variable-name">interfaceOrientation</span>
{
    <span class="comment-delimiter">// </span><span class="comment">Return YES for supported orientations
</span>    <span class="keyword">return</span> (interfaceOrientation == UIInterfaceOrientationPortrait);
}

- (<span class="type">void</span>)<span class="function-name">didReceiveMemoryWarning</span>
{
    <span class="comment-delimiter">// </span><span class="comment">Releases the view if it doesn't have a superview.
</span>    [<span class="keyword">super</span> didReceiveMemoryWarning];

    <span class="comment-delimiter">// </span><span class="comment">Release any cached data, images, etc. that aren't in use.
</span>}

- (<span class="type">void</span>)<span class="function-name">viewDidUnload</span>
{
    [<span class="keyword">super</span> viewDidUnload];

    <span class="comment-delimiter">// </span><span class="comment">Release any retained subviews of the main view.
</span>    <span class="comment-delimiter">// </span><span class="comment">e.g. self.myOutlet = nil;
</span>}

- (<span class="type">void</span>)<span class="function-name">dealloc</span>
{
    bannerView_.delegate = <span class="constant">nil</span>;
    [bannerView_ release];

    [<span class="keyword">super</span> dealloc];
}

- (<span class="type">void</span>)<span class="function-name">adViewDidReceiveAd</span>:(<span class="type">GADBannerView</span> *)<span class="variable-name">view</span> {
    [<span class="type">UIView</span> animateWithDuration:ANIMATION_DURATION
                     animations:^{
                         bannerView_.center = CGPointMake(bannerView_.center.x, bannerView_.center.y-TABBAR_HEIGHT);
                     }];
}

<span class="keyword">@end</span>
</pre>
</td>
<tr></table>
</div>
<p>最终效果如下。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/iphone/AdMob13.png" rel="lightbox[433]"><br />
<img src="http://www.yifeiyang.net/images/iphone/AdMob13.png" alt="Admob" /></a>
</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/iphone-development-skills-of-the-articles-published-5-add-an-ad-in-the-program/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iPhone开发技巧之环境篇（10）&#8212; 在控制台调试iPhone应用程序</title>
		<link>http://www.yifeiyang.net/iphone-development-skills-of-the-environment-chapter-10-iphone-application-in-the-debug-console/</link>
		<comments>http://www.yifeiyang.net/iphone-development-skills-of-the-environment-chapter-10-iphone-application-in-the-debug-console/#comments</comments>
		<pubDate>Sun, 13 Nov 2011 01:26:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[开发技巧]]></category>
		<category><![CDATA[环境篇]]></category>
		<category><![CDATA[Debug]]></category>
		<category><![CDATA[Emacs]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/iphone%e5%bc%80%e5%8f%91%e6%8a%80%e5%b7%a7%e4%b9%8b%e7%8e%af%e5%a2%83%e7%af%87%ef%bc%8810%ef%bc%89-%e5%9c%a8%e6%8e%a7%e5%88%b6%e5%8f%b0%e8%b0%83%e8%af%95iphone%e5%ba%94%e7%94%a8%e7%a8%8b%e5%ba%8f/</guid>
		<description><![CDATA[<p style="text-align: center;"><strong>iPhone开发技巧之环境篇（10）— 在控制台调试iPhone应用程序</strong></p>
<p>使用Xcode调试程序的时候，是否觉得有时候运行速度太慢。用Xcodebuild编译好程序之后，Xcode作为IDE纯粹是为了debug，使用iPhone模拟器而用的。其实iPhone模拟器只是Xcode自带的一个工具，不需要[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/iphone-development-skills-of-the-environment-chapter-10-iphone-application-in-the-debug-console/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<p style="text-align: center;"><strong>iPhone开发技巧之环境篇（10）— 在控制台调试iPhone应用程序</strong></p>
<p>使用Xcode调试程序的时候，是否觉得有时候运行速度太慢。用Xcodebuild编译好程序之后，Xcode作为IDE纯粹是为了debug，使用iPhone模拟器而用的。其实iPhone模拟器只是Xcode自带的一个工具，不需要Xcode也可以启动的。这里，我们来看看一个叫做<a href="https://github.com/jhaynie/iphonesim">iphonesim</a>的东东，通过控制台来模拟调试我们的iPhone程序。有了它就可以解决Xcode过于庞大，运行速度慢的问题。</p>
<p>首先，我们从<a href="https://github.com/jhaynie/iphonesim">github</a>上下载了程序源码，通过Xcode编译。这之后iphonesim接生成了，可以通过命令行来使用它。</p>
<dl>
<dt><strong>比如我调试一个Test.app的程序</strong></dt>
<dd></dd>
</dl>
<div class="codebox">
<table border="0" width="100%">
<tbody>
<tr>
<td class="line_numbers">
<pre>1
2
3
4
5
6</pre>
</td>
<td>
<pre class="src" style="color:#f5deb3;"> $ pwd
 /Users/Tester/Downloads/iphonesim/build/Debug
 $ ./iphonesim launch ~/Desktop/Fac/Test/build/Debug-iphonesimulator/Test.app
 [DEBUG] App Spec: 〜
 [DEBUG] SDK Root: 〜
 [DEBUG] Session started</pre>
</td>
</tr>
</tbody>
</table>
</div>
<p>OK, 从控制台我们启动了iPhone模拟器，这并没有Xcode的参与。这之后可以使用iphonesim+gdb的组合来调试我们的程序了。</p>
<p>结合之前<a href="http://www.yifeiyang.net/iphone-development-techniques-of-environmental-articles-1-use-emacs-development-iphone-application/">iPhone开发技巧之环境篇（1）--- 使用Emacs开发iPhone应用程序</a>中介绍的那样，我们就可以实现Xcodebuild+iphonesim+gdb的完美组合，不需要启动又大又重的Xcode。</p>
<p>通过控制台调用模拟器的方法，我们可以很轻松地实现自动化的程序单体测试，产品发布前的结合测试...</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/iphone-development-skills-of-the-environment-chapter-10-iphone-application-in-the-debug-console/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>敏捷开发实践（1） &#8212; 走进敏捷软件开发</title>
		<link>http://www.yifeiyang.net/agile-development-practices-1-about-agile-software-development/</link>
		<comments>http://www.yifeiyang.net/agile-development-practices-1-about-agile-software-development/#comments</comments>
		<pubDate>Wed, 09 Nov 2011 01:36:00 +0000</pubDate>
		<dc:creator>yang</dc:creator>
				<category><![CDATA[敏捷开发]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Google]]></category>

		<guid isPermaLink="false">http://www.yifeiyang.net/%e6%95%8f%e6%8d%b7%e5%bc%80%e5%8f%91%e5%ae%9e%e8%b7%b5%ef%bc%881%ef%bc%89-%e8%b5%b0%e8%bf%9b%e6%95%8f%e6%8d%b7%e8%bd%af%e4%bb%b6%e5%bc%80%e5%8f%91/</guid>
		<description><![CDATA[<div class="contents">
<dt> <a href="#sec1">敏捷开发是什么</a> </dt>
<dt> <a href="#sec2">为什么要使用敏捷开发</a> </dt>
<dt> <a href="#sec3">敏捷开发的现状与将来</a> </dt>
</div>
<p style="text-align: center;"> <strong>敏捷开发实践（1） &#8212; 走进敏捷软件开发</strong> </p>
<h3><a name="sec1" id="sec1"></a>敏捷开发是什么</h3>
<p class="first">敏捷软件开发是一个概念意义上的框架，用来取代软件工程项目的概念；它强调在项目的整个生命周期中，拥抱并促进由于软件进化式的发展所带来的变化。</p>
<p>简单的说，[......]</p><p class='read-more'><a href='http://www.yifeiyang.net/agile-development-practices-1-about-agile-software-development/'>继续阅读</a></p>]]></description>
			<content:encoded><![CDATA[<div class="contents">
<dt> <a href="#sec1">敏捷开发是什么</a> </dt>
<dt> <a href="#sec2">为什么要使用敏捷开发</a> </dt>
<dt> <a href="#sec3">敏捷开发的现状与将来</a> </dt>
</p></div>
<p style="text-align: center;"> <strong>敏捷开发实践（1） &mdash; 走进敏捷软件开发</strong> </p>
<h3><a name="sec1" id="sec1"></a>敏捷开发是什么</h3>
<p class="first">敏捷软件开发是一个概念意义上的框架，用来取代软件工程项目的概念；它强调在项目的整个生命周期中，拥抱并促进由于软件进化式的发展所带来的变化。</p>
<p>简单的说，敏捷开发是一种以人为核心、迭代、循序渐进的开发方法。在敏捷开发中，软件项目的构建被切分成多个子项目，各个子项目的成果都经过测试，具备集成和可运行的特征。换言之，就是把一个大项目分为多个相互联系，但也可独立运行的小项目，并分别完成，在此过程中软件一直处于可使用状态。</p>
<p>敏捷开发中涵盖多种开发元素：迭代开发，增量交付，测试驱动，结对编程，Usecase驱动，持续集成，重构等。我们知道软件开发项目中式样变更，需求变更是影响开发进度、成败的重要因素，而敏捷开发元素的利用可以有效地规避这些风险、变化。学会如何识别变化的大势，并在可能的时候，促使变化向好的方向发展。这是软件项目进化的趋势。</p>
<dl>
<dt><strong>最后我们看看敏捷开发宣言</strong></dt>
<dd></dd>
</dl>
<ul>
<li>个体与交互 <strong>重于</strong> 过程和工具</li>
<li>可用的软件 <strong>重于</strong> 完备的文档</li>
<li>客户协作   <strong>重于</strong> 合同谈判</li>
<li>响应变化   <strong>重于</strong> 遵循计划</li>
</ul>
<dl>
<dt><strong>同时，遵循以下准则</strong></dt>
<dd></dd>
</dl>
<ul>
<li>我们的最高目标是，通过尽早和持续地交付有价值的软件来满足客户。</li>
<li>欢迎对需求提出变更——即使是在项目开发后期。要善于利用需求变更，帮助客户获得竞争优势。</li>
<li>要不断交付可用的软件，周期从几周到几个月不等，且越短越好。</li>
<li>项目过程中，业务人员与开发人员必须在一起工作。</li>
<li>要善于激励项目人员，给他们以所需要的环境和支持，并相信他们能够完成任务。</li>
<li>无论是团队内还是团队间，最有效的沟通方法是面对面的交谈。</li>
<li>可用的软件是衡量进度的主要指标。</li>
<li>敏捷过程提倡可持续的开发。项目方、开发人员和用户应该能够保持恒久稳定的进展速度。</li>
<li>对技术的精益求精以及对设计的不断完善将提升敏捷性。</li>
<li>要做到简洁，即尽最大可能减少不必要的工作。这是一门艺术。</li>
<li>最佳的架构、需求和设计出自于自组织的团队。</li>
<li>团队要定期反省如何能够做到更有效，并相应地调整团队的行为。</li>
</ul>
<h3><a name="sec2" id="sec2"></a>为什么要使用敏捷开发</h3>
<p class="first">对于有一定经验的软件开发人员，是否有过疲惫应对式样变更，需求变更而导致返工的经历。</p>
<dl>
<dt><strong>变化的原因无非是</strong></dt>
<dd></dd>
</dl>
<ul>
<li>项目开始阶段对需求并不明确，就急于开发设计</li>
<li>产品已不能按照项目启动时定义的要求发布
<ul>
<li>最终出来的产品往往和最初设计，构思的完全不同；变更之前定义的要求/需求将是很正常的事情</li>
<li>大家都不关心产品的制作过程，最初的成果物往往都是废品；然后是在废品上的修修补补
<ul>
<li>软件开发以外的部门并不认真对待软件开发，一味地要求结果，要求性能</li>
<li>软件确实是可以重新编写的，但是已经用掉的工数是不会取回的</li>
</ul>
</li>
</ul>
</li>
<li>如果不试着先做做，是不知道是否能实现
<ul>
<li>新的硬件是否能按照式样中那样描述的工作，达到理想的性能指标</li>
<li>团队成员是否都熟悉开发工具，达到熟悉程度为止的培训要花相应的时间</li>
<li>软件架构是否稳定，需要早期验证设计的合理性</li>
</ul>
</li>
</ul>
<p>综上，如果按照我们习惯的瀑布流式的开发方式，是否能应对这些变化的因素呢？我们来看一下瀑布式开发的典型V字型模型。</p>
<div class="imagebox">
<p><a href="http://www.yifeiyang.net/images/agile/v-develop.gif" rel="lightbox[428]"> <img src="http://www.yifeiyang.net/images/agile/v-develop.gif" alt="v-develop" /></a> </p>
</div>
<p>一开始，我们先有完整的用户需求，定义，接下来做需求分析，概要设计，详细设计；然后到编码，单元测试，集成测试，系统测试，验收。看似很理想的过程，可是在实践当中常常出现这种这样的问题。</p>
<p>首先我们的用户也许并不了解自己需要什么，所以常常到了编码阶段，甚至是测试阶段，才提出各种各样的修改；对于用户很轻松的事情，可对于我们开发者来说简直就是地狱。另外，产品开发都是希望尽快完成开发任务，尽早投放市场。可是开发的风险是不可避免的，一般软件开发都很难按时按质完成。</p>
<p>降低风险是敏捷开发中很重要的一环。机能分割，迭代式开发等敏捷元素的利用，有效地降低了开发中存在的风险。一方面随着迭代式开发，可以随时验证架构设计的稳妥性，另一方面可以应对即时的需求变更。</p>
<h3><a name="sec3" id="sec3"></a>敏捷开发的现状与将来</h3>
<p class="first">敏捷开发概念从2004年初开始广为流行。近几年，敏捷软件开发在软件工业界有了良好的发展势头并逐渐被推广开来。一些著名的公司如 Google, Microsoft 和 Yahoo 和众多的中小公司都已经开始采用敏捷开发。</p>
<p>敏捷方法给这些企业也已带来了巨大的收益。据业内资深人士和长期从事敏捷咨询的服务公司透露，采用敏捷开发的团队一般会提高3-10倍的效率，软件的质量也有了更加可靠的保证。同时，敏捷开发的应用也给团队内的每个成员提供了良好的发展机会。他们的技术和合作水平都能得到响应的提高。</p>
<p>欧美软件企业中，有近半企业已采用敏捷方法进行开发。大多数尚未应用敏捷的企业，也都对其有所了解，而且很多在计划实施。中国的外企，外包公司和许多知名企业也都开始采用了敏捷方法。</p>
<p>敏捷开发不仅适用于少人数的团队协作，在大规模开发，离岸开发中也发挥着重要的作用。虽然它不是软件开发领域中的“银弹”，但是理解了其精髓，在降低产品发布的风险，构建协作团队等方面都是有帮助的。</p>
<p>同时，敏捷总是在不断发展演变，因此，没有一个人能保证目前的敏捷方法都是正确的。每个采用敏捷开发的团队都可以通过发现并形成自己的想法和最佳实践，对敏捷开发做出自己的贡献。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.yifeiyang.net/agile-development-practices-1-about-agile-software-development/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

