-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
798 lines (481 loc) · 33.1 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Oolong Tea's blog</title>
<meta name="author" content="yechunxiao19">
<meta name="description" content="ios的多线程管理有3种方式 NSThread & Run Loop
NSOperation
GCD Run Loop 首先我们说一下线程的起用和退出的问题,当我们自己创建一个线程并分配给它活干的时候,它会立刻开始给我们干活,一旦活干完了,它又没有马上找到新活,那么就会立刻退出, …">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link href="/atom.xml" rel="alternate" title="Oolong Tea's blog" type="application/atom+xml">
<link rel="canonical" href="">
<link href="/favicon.png" rel="shortcut icon">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
<script async="true" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
</head>
<body>
<header id="header" class="inner"><h1><a href="/">Oolong Tea's blog</a></h1>
<nav id="main-nav"><ul class="main">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<nav id="mobile-nav">
<div class="alignleft menu">
<a class="button">Menu</a>
<div class="container"><ul class="main">
<li><a href="/">Blog</a></li>
<li><a href="/blog/archives">Archives</a></li>
<li><a href="/about">About</a></li>
</ul>
</div>
</div>
<div class="alignright search">
<a class="button"></a>
<div class="container">
<form action="http://google.com/search" method="get">
<input type="text" name="q" results="0">
<input type="hidden" name="q" value="site:yechunxiao19.github.com">
</form>
</div>
</div>
</nav>
<nav id="sub-nav" class="alignright">
<div class="social">
<a class="github" href="https://github.com/yechunxiao19" title="GitHub">GitHub</a>
<a class="rss" href="/atom.xml" title="RSS">RSS</a>
</div>
<form class="search" action="http://google.com/search" method="get">
<input class="alignright" type="text" name="q" results="0">
<input type="hidden" name="q" value="site:yechunxiao19.github.com">
</form>
</nav>
</header>
<div id="content" class="inner">
<article class="post">
<h2 class="title">
<a href="/blog/2014/02/04/gcd/">
IOS并发编程</a>
</h2>
<div class="entry-content">
<p>ios的多线程管理有3种方式</p>
<ol>
<li>NSThread & Run Loop</li>
<li>NSOperation</li>
<li>GCD</li>
</ol>
<h4>Run Loop</h4>
<p>首先我们说一下线程的起用和退出的问题,当我们自己创建一个线程并分配给它活干的时候,它会立刻开始给我们干活,一旦活干完了,它又没有马上找到新活,那么就会立刻退出,这个线程就结束了。注意,这里是它一旦发现自己没活可干,就会马上消失,片刻都不会停留。</p>
<p>这样我们就遇到一个问题,如果我们打算让这个线程做一个延时的任务,或者想让它接受其它的回调命令,或者等待一个点击等非即时性命令,而这个线程是不知道等的,因为它一发现自己没活干,就消失了。这显然不是我们希望的。可能有人会问,主线程不会这样啊,原因就是我们要讨论的主题,主线程默认开启了RunLoop,而我们创建的线程默认是没有开启的。</p>
<p>因此,如果我们要在非主线程执行一些非即时性的事情,就必须手动开启RunLoop,它一旦开启,线程就会开启监听状态,这样线程便不会退出,而是转入休息状态,RunLoop负责把风,一旦发现活来了,就通知线程开始干活。如果我们确认这个线程再也不需要在处理任何非即时性事件时,可以停止RunLoop,这时候线程就再看看手头有没有现活,有继续做,没有就立刻退出。</p>
<p><img src="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/Art/runloop.jpg" alt="" /></p>
<p>输入源传递异步消息给相应的处理例程,并调用<code>runUntilDate:</code>方法来退出(在线程里面相关的<code>NSRunLoop</code>对象调用)。定时源则直接传递消息给处理例程,但并不会退出run loop。</p>
<p>(未完待续,这块还是个半吊子。。。。。。)</p>
<h4>NSThread</h4>
<p>NSThread 比其他两种方式轻量级,但需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁也会有一定的系统开销。</p>
<p>使用NSThread来创建线程有多个方法:</p>
<ol>
<li>使用 <code>+ (void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument</code> 类方法来生成一个新的线程。</li>
<li>使用 <code>- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument</code> 创建一个新的 NSThread 对象,并调用它的 start 方法。</li>
<li>调用NSObject的<code>+performSelectorInBackground:withObject:</code>方法生成子线程。</li>
<li>创建一个NSThread子类,然后调用子类实例的start方法。</li>
</ol>
<p>编写你线程的主体入口点的额外步骤:</p>
<ol>
<li>在<a href="http://yechunxiao19.github.io/blog/2014/02/04/arc/">ARC</a>这篇文章里面提到了,有3种情况应该自己创建autorelease池,其中一种情况就是创建了子线程。</li>
<li>子线程应该自行管理好抛出异常,如果不在子线程内部设置异常处理函数,会导致程序直接Crash。</li>
<li>如果子线程不止一次操作,需要循环处理的话,设置一个Run Loop。</li>
</ol>
<p>每个线程都维护了一个键-值的字典,它可以在线程里面的任何地方被访问。你可以使用该字典来保存一些信息,这些信息在整个线程的执行过程中都保持不变。你使用NSThread的<code>threadDictionary</code>方法来检索一个NSMutableDictionary对象,你可以在它里面添加任何线程需要的键。</p>
<p>你创建的任何线程默认的优先级是和你本身线程相同。你可以使用NSThread的 <code>setThreadPriority:</code>类方法来设置当前运行线程的优先级。</p>
<p>退出一个线程推荐的方法是让它从主题入口点正常退出。对于直接杀死线程,这是完全不鼓励的,这样无法保证线程当前使用资源被清理干净了。如果需要在操作中间中断一个线程,建议使用Run Loop的输入源来接收取消消息。</p>
<h4>NSOperation</h4>
<p>NSOperation本身是抽象基类,我们必须实现子类。Foundation framework提供NSInvocationOperation和NSBlockOperation两个具体子类,你可以直接使用。</p>
<p>所有 operation objects 都支持以下关键特性:</p>
<ol>
<li>支持建立基于图的operation objects依赖。可以阻止某个operation 运行,直到它依赖的所有 operation 都已经完成。</li>
<li>支持可选的 completion block,在 operation 的主任务完成后调用。</li>
<li>支持应用使用 KVO 通知来监控 operation 的执行状态。</li>
<li>支持 operation 优先级,从而影响相对的执行顺序。</li>
<li>支持取消,允许你中止正在执行的任务。</li>
</ol>
<p>创建一个 NSInvocationOperation 对象并发执行</p>
<pre><code>@implementation MyCustomClass
- (NSOperation*)taskWithData:(id)data {
NSInvocationOperation* theOp = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(myTaskMethod:) object:data];
return theOp;
}
// This is the method that does the actual work of the task.
- (void)myTaskMethod:(id)data {
// Perform the task.
}
@end
</code></pre>
<p>创建一个NSBlockOperation 对象</p>
<pre><code>NSBlockOperation* theOp = [NSBlockOperation blockOperationWithBlock: ^{
NSLog(@"Beginning operation.\n");
// Do some work.
}];
</code></pre>
<p>使用<code>addExecutionBlock:</code>可以添加更多block到这个block operation对象。如果需要顺序地执行 block,你必须直接提交到所需的dispatch queue。</p>
<p>自定义operation需要实现的方法:</p>
<ol>
<li>自定义<code>initialization</code>方法:初始化,将operation对象设置为已知状态(必须实现)</li>
<li>自定义<code>main</code>方法:执行你的任务(必须实现)</li>
<li><code>main</code>方法中需要调用的其它自定义方法</li>
<li><code>Accessor</code>方法:设置和访问operation对象的数据</li>
<li><code>NSCoding</code>协议的方法:允许operation对象archive和unarchive</li>
</ol>
<p>operation响应取消,调用<code>isCancelled:</code>方法,如果返回YES(表示已取消),则立即退出执行。以下地方可能需要调用 isCancelled:g</p>
<ul>
<li>在执行任何实际的工作之前</li>
<li>在循环的每次迭代过程中,如果每个迭代相对较长可能需要调用多次</li>
<li>代码中相对比较容易中止操作的任何地方</li>
</ul>
<p>Operation对象默认按同步方式执行,也就是在调用<code>start</code>方法的那个线程中直接执行。但是如果你希望异步执行操作,你就必须定义operation对象为并发操作来实现。</p>
<ul>
<li><code>start</code> (必须)所有并发操作都必须覆盖这个方法,以自定义的实现替换默认行为。手动执行一个操作时,你会调用<code>start</code>方法。因此你对这个方法的实现是操作的起点,设置一个线程或其它执行环境,来执行你的任务。你的实现在任何时候都绝对不能调用<code>super</code>。</li>
<li><code>main</code> (可选)这个方法通常用来实现operation对象相关联的任务。尽管你可以在<code>start</code>方法中执行任务,使用<code>main</code>来实现任务可以让你的代码更加清晰地分离设置和任务代码。</li>
<li><code>isExecuting</code> <code>isFinished</code> (必须)并发操作负责设置自己的执行环境,并向外部 client 报告执行环境的状态。因此并发操作必须维护某些状态信息,以知道是否正在执行任务,是否已经完成任务。使用这两个方法报告自己的状态。这两个方法的实现必须能够在其它多个线程中同时调用。另外这些方法报告的状态变化时,还需要为相应的key path产生适当的KVO通知。</li>
<li><code>isConcurrent</code> (必须)标识一个操作是否并发 operation,覆盖这个方法并返回 YES。</li>
</ul>
<p>使用 NSOperation 的<code>addDependency:</code>方法在两个operation对象之间建立依赖关系。表示当前operation 对象将依赖于参数指定的目标 operation 对象。如果你自定义了 operation 对象的行为,就必须在自定义代码中生成适当的 KVO 通知,以确保依赖能够正确地执行。</p>
<p>operation 可以在主任务完成之后执行一个 <code>completion block</code>。你可以使用这个 <code>completion block</code> 来执行任何不属于主任务的工作。</p>
<p>执行Operations有2种方法:</p>
<ol>
<li>执行 Operations 最简单的方法是添加到 operation queue</li>
<li>手动执行 Operation,要求 Operation 已经准备好,<code>isReady</code>返回 YES,此时你才能调用<code>start</code>方法来执行</li>
</ol>
<p><a href="https://developer.apple.com/library/mac/samplecode/NSOperationSample/Introduction/Intro.html#//apple_ref/doc/uid/DTS10004184">NSOperation的官方例程</a></p>
<h4>GCD</h4>
<p>GCD dispatch queues 是执行任务的强大工具,允许你同步或异步地执行任意代码block。</p>
<p>目前GCD中有三种类型的Dispatch Queue:</p>
<ol>
<li>Main Queue:关联到主线程的队列,可以使用函数<code>dispatch_get_main_queue()</code>获得,加到这个队列中的工作都会分发到主线程运行。主线程只有一个,因此很明显这个是串行队列,每次运行一个工作。</li>
<li>Global Queue:全局队列是并发队列,又根据优先级细分为高优先级、默认优先级和低优先级三种。通过 <code>dispatch_get_global_queue</code> 加上优先级参数获得这个全局队列,例如 <code>dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)</code></li>
<li>自定义Queue:自己创建一个队列,通过函数 <code>dispatch_queue_create</code> 创建,例如 <code>dispatch_queue_create("com.kiloapp.test", NULL)</code> 。第一个参数是队列的名字,Apple建议使用反DNS型的名字命名,防止重名;第二个参数是创建的queue的类型,iOS 4.3以前只支持串行,即DISPATCH_QUEUE_SERIAL(就是NULL),iOS4.3以后也开始支持并行队列,即参数DISPATCH_QUEUE_CONCURRENT。</li>
</ol>
<p>添加单个任务到Queue:</p>
<ul>
<li><code>dispatch_async</code> 和 <code>dispatch_async_f</code> 函数异步添加</li>
<li><code>dispatch_sync</code> 和 <code>dispatch_sync_f</code> 函数同步添加</li>
<li><code>dispatch_after</code> 同步延迟添加</li>
</ul>
</div>
<div class="meta">
<div class="date">
<time datetime="2014-02-04T22:31:52+08:00" pubdate data-updated="true">Feb 4<span>th</span>, 2014</time></div>
<div class="tags">
<a class='category' href='/blog/categories/ios/'>ios</a>
</div>
<div class="comments"><a href="/blog/2014/02/04/gcd/#comments">Comments</a></div>
</div>
</article>
<article class="post">
<h2 class="title">
<a href="/blog/2014/02/04/blocks/">
Blocks</a>
</h2>
<div class="entry-content">
<p>Blocks是C语言的新拓展,一句话来概括的话,可以理解为“anonymous functions together with automatic (local) variables.” Blocks有点像函数,但是它可以在其它函数或方法中进行声明和定义,同时它还是匿名的(匿名函数),并可以捕获其所在作用域中的变量(闭包特性)。</p>
<p>使用^操作符来来声明一个block变量和指示block文本的开始。Block本身的主体被{}包含着,如下面的例子那样</p>
<pre><code>int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
</code></pre>
<p><img src="https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Blocks/Art/blocks.jpg" alt="" /></p>
<p>如果你没有显式的给block表达式声明一个返回值,它会自动的从block内容推断出来。如果返回值是推断的,而且参数列表也是void,那么你同样可以省略参数列表的void。如果或者当出现多个返回状态的时候,它们必须是完全匹配的(如果有必要可以使用强制转换)。</p>
<p>block有点像函数,事实上它确实可以像函数一样使用</p>
<pre><code>int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
printf("%d", myBlock(3));
// prints "21"
</code></pre>
<p>block也可以作为函数的参数</p>
<pre><code>char *myCharacters[3] = { "TomJohn", "George", "Charles Condomine" };
qsort_b(myCharacters, 3, sizeof(char *), ^(const void *l, const void *r) {
char *left = *(char **)l;
char *right = *(char **)r;
return strncmp(left, right, 1);
});
</code></pre>
<p>block有闭包的特性,所以可以捕获所在作用域的变量</p>
<pre><code>void testBlock() {
int a = 1;
int b = 2;
int (^aBlock)(void) = ^ { return a + b; };
printf("%d\n", aBlock()); // 输出 3
a = 0;
printf("%d\n", aBlock()); // 还是输出 3
}
</code></pre>
<p>需要注意的是,两次输出的值都为3,即使在第二次输出前我们已经将a的值赋为0。这是因为在定义aBlock时编译器已经对a和b的值作了一个const拷贝(你不能在aBlock中修改a的值)并保存,导致后续外部对a的修改没有影响到aBlock的执行结果。如果想在aBlock中通过引用访问a或者修改a的值,你需要在a的声明前加上一个限定词<code>__block</code>:</p>
<pre><code>void testBlock() {
__block int a = 1;
int b = 2;
int (^aBlock)(void) = ^ { return a + b; };
a = 0;
printf("%d\n", aBlock()); // 输出 2
}
</code></pre>
<p>这样,使用该限定词的变量会通过引用的方式传入Block,使得它的值可以在Block执行后被修改。这样的变量通常是保存在栈中的,但是如果引用该变量的Block被拷贝,它也会随之被拷贝到堆中。使用<code>__block</code>的变量有两个限制,它们不能是可变长的数组,并且它们不能是包含有C99可变长度的数组变量的数据结构.</p>
<p>Blocks 通常代表一个很小、自包的代码片段。因此它们作为封装的工作单元在并 发执行,或在一个集合项上,或当其他操作完成时的回调的时候非常实用。</p>
<p>在引用计数的环境里面,默认情况下当你在block里面引用一个Objective-C对象的时候,该对象会被retain。当你简单的引用了一个对象的实例变量时,它同样被retain。但是被 <code>__block</code> 存储类型修饰符标记的对象变量不会被 retain。在垃圾回收机制里面,如果你同时使用 <code>__weak</code> 和 <code>__block</code> 来标识一个变量,那么该block将不会保证它是一直是有效的。</p>
<p>如果你在实现方法的时候使用了 block,对象的内存管理规则更微妙:</p>
<ul>
<li>如果你通过引用来访问一个实例变量,self 会被 retain。</li>
<li>如果你通过值来访问一个实例变量,那么变量会被 retain。</li>
</ul>
</div>
<div class="meta">
<div class="date">
<time datetime="2014-02-04T22:31:13+08:00" pubdate data-updated="true">Feb 4<span>th</span>, 2014</time></div>
<div class="tags">
<a class='category' href='/blog/categories/ios/'>ios</a>
</div>
<div class="comments"><a href="/blog/2014/02/04/blocks/#comments">Comments</a></div>
</div>
</article>
<article class="post">
<h2 class="title">
<a href="/blog/2014/02/04/arc/">
ARC</a>
</h2>
<div class="entry-content">
<h3>ARC以前的生活</h3>
<h4>reference counting</h4>
<p>在ARC以前,是手动内存管理,但不论是自动的,还是手动的内存管理,reference counting始终是内存管理的一个核心内容。<br/>
用办公室的照明管理来类比说明一下reference counting的原理。</p>
<p>按以下规则来安排照明灯的管理</p>
<ol>
<li>当某人进入办公室时,办公室是空,则他负责打开灯</li>
<li>之后有人进入办公室,照明灯继续使用</li>
<li>当某人离开办公室,他就不再需要照明灯了</li>
<li>当最后一个人离开了,他关掉照明灯</li>
</ol>
<p>我们量化的来管理照明灯系统,使用counter来计数</p>
<ol>
<li>当某人进入办公室时,办公室是空,counter +1,它由0变成了1,所以打开灯</li>
<li>当另外的人进来,counter +1,它由1变成2</li>
<li>当某人离开,counter -1,它由2变成1</li>
<li>当最后一个人离开,counter变成了0,则关掉灯</li>
</ol>
<p><img src="https://7muh2q.bn1304.livefilestore.com/y2pXcCfTG3S9vsiAkh20ujVkLRhHCGAIig2RNc5MwlBBC5RFrhGtDdRS22N2uGKZ8WC0NfQAWA-CzAdVaOWaPqNIQqs1GFABKDObkYhuG7HJxE/E4D8A866-C198-466D-BD3F-9B038B7881C0.png?psid=1" alt="Drawing" width="600px"/></p>
<p>reference counting的原理跟照明灯的管理是一样的</p>
<ol>
<li>打开灯 == 创建(<code>alloc/new/copy/mutableCopy group</code>)一个Objective-C对象并使用它 <code>reference counting = 1</code></li>
<li>又进来一个人使用灯 == 取得(<code>retain</code>)Objective-C对象所有权 <code>reference counting = 2</code></li>
<li>有一个人离开,不再使用灯 == 放弃(<code>release</code>)Objective-C对象所有权 <code>reference counting = 1</code></li>
<li>最后一个人离开,关掉灯 == 丢弃(<code>deallo</code>c)Objective-C对象 <code>reference counting = 0</code></li>
</ol>
<p>对于新创建的对象,方法名以<code>alloc/new/copy/mutableCopy group</code>开头的,才拥有这个对象,才有责任负责释放它。</p>
<h4>实现对象的 dealloc</h4>
<p>NSObject 类定义了一个名为 <code>dealloc</code> 的方法。这个方法在对象无主(没有所有者)的情况下, 当内存回收的时候会由系统自动调用。<code>dealloc</code> 方法的作用就是释放对象的内存,并弃掉它持有的任何资源—-以及它对其他对象的所有权。</p>
<pre><code>- (void)dealloc{
[_firstName release];
[super dealloc];
}
</code></pre>
<h4>手动管理内存的限定符</h4>
<ul>
<li><code>assign</code> 对于NSInteger、CGPoint、C数据类型等,这些直接在栈上开辟的内存,无需管理他们内存计数器,使用assign比较合适</li>
<li><code>retain</code> 对于在堆中开辟的内存,我们需要维护内存的计数器。</li>
<li><code>copy</code> 如果指针A和指针B不想相互牵扯,A管理A的内存,B管理B的内存,copy正是为这个而生。</li>
</ul>
<h4>Autorelease</h4>
<p>Autorelease从名字来看,你也许会认为它类似于ARC,但其实不是这样的,它更类似于C语言的局部变量。对于Autorelease,你可以像局部变量一样使用对象,这意味着当执行离开代码块,对象将自动调用<code>release</code>方法。</p>
<pre><code>NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [[NSObject alloc] init];
[obj autorelease];
[pool drain];
</code></pre>
<p>在以上代码的最后,<code>[pool drain]</code>将执行<code>[obj release]</code>。
Cocoa 希望程序中长期存在一个 autorelease 池。如果池不存在,autorelease 的对象就无从 release 了,从而造成内存泄露。当程序中没有 autorelease 池,你的程序还给对象发送 autorelease 消息,这是 Cocoa 会发出一个错误日志。AppKit 和 UIKit 框架自动在每个消息循环的开始都创建一个池(比如鼠标按下事件、触摸事件)并在结尾处销毁这个池。正因为如此,你实际上不需要创建 autorelease 池,甚至不需要知道创建 autorelease 池的代码如何写。下面三种情形下,你却应该使用你自己的 autorelease 池:</p>
<ol>
<li>如果你写的程序,不是基于 UI Framwork。例如你写的是一个基于命令行的程序。</li>
<li>如果你程序中的一个循环,在循环体中创建了大量的临时对象。你可以在循环体内部新建一个 autorelease 池,并在一次循环结束时销毁这些临时对象。这样可以减少你的程序对内存的占用峰值。</li>
<li>如果你发起了一个 secondary 线程(main 线程之外的线程)。这时你“必须”在线程的最初执行代码中创建 autorelease 池,否则你的程序就内存泄露了。</li>
</ol>
<p>Autorelease 池常常被称为“嵌套”的,但实际上也可以把这些嵌套的池理解为在一个栈中。当一个对象收到了 autorelease 消息时,又或者这个对象 作为 addObject:方法的参数传递的时候,这个对象被放到了当时这个栈中最顶端的那个池中。</p>
<h3>ARC</h3>
<p>在工程中使用ARC非常简单:只需要像往常那样编写代码,只不过永远不写<code>retain</code>,<code>release</code>和<code>autorelease</code>三个关键字就好。
ARC是Objective-C编译器的特性,而不是运行时特性或者垃圾回收机制,ARC所做的只不过是在代码编译时为你自动在合适的位置插入<code>release</code>或<code>autorelease</code>,就如同之前手动内存管理时你所做的那样。因此,至少在效率上ARC机制是不会比手动内存管理弱的,而因为可以在最合适的地方完成reference counting的维护,以及部分优化,使用ARC甚至能比MRC取得更高的运行效率。</p>
<h4>ARC下有以下4个限定符</h4>
<ul>
<li><code>strong</code> 跟retain一样,在ARC下,如果对象没有任何strong指针指向,那么就将被销毁</li>
<li><code>weak</code> weak指针指向的对象reference counting不会增加,对象无strong指针指向时,对象会被销毁,在ARC机制作用下,所有指向这个对象的weak指针将被置为nil。delegate、outlet多使用weak</li>
<li><code>unsafe_unretained</code> 这就是原来的assign。当需要支持iOS4时需要用到这个关键字</li>
<li><code>autoreleasing</code> 这个就等于手动内存管理的Autorelease</li>
</ul>
<p>上文Autorelease池的代码在ARC下就转换为:</p>
<pre><code>@autoreleasepool {
id __autoreleasing obj = [[NSObject alloc] init];
}
</code></pre>
<h4>桥接</h4>
<p>ARC只适用于NSObject,底层对象依旧需要进行手动内存管理。在ARC中,编译器需要知道这些底层对象指针应该由谁来负责释放,这时就要进行桥接处理。</p>
<ul>
<li><code>__bridge</code> 只做类型转换,不改变对象所有权,是我们最常用的转换符。</li>
<li><code>__bridge_retained</code> 将Objective-C对象转换为Core Foundation对象,把对象所有权桥接给Core Foundation对象,同时剥夺ARC的管理权,后续需要开发者使用CFRelease或者相关方法手动来释放对象。</li>
<li><code>__bridge_transfer</code> 将非Objective-C对象转换为Objective-C对象,同时将对象的管理权交给ARC,开发者无需手动管理内存。</li>
</ul>
<p>从OC转CF,ARC管理内存</p>
<pre><code>NSString *aNSString = [[NSString alloc]initWithFormat:@"test"];
CFStringRef aCFString = (__bridge CFStringRef)aNSString;
(void)aNSString
</code></pre>
<p>从CF转OC,需要开发者手动释放,不归ARC管(void* id的转换)</p>
<pre><code>CFStringRef aCFString = CFStringCreateWithCString(NULL, "test", kCFStringEncodingASCII);
NSString *aNSString = (__bridge NSString *)aCFString;
(void)aNSString;
CFRelease(aCFString);
</code></pre>
</div>
<div class="meta">
<div class="date">
<time datetime="2014-02-04T22:30:48+08:00" pubdate data-updated="true">Feb 4<span>th</span>, 2014</time></div>
<div class="tags">
<a class='category' href='/blog/categories/ios/'>ios</a>
</div>
<div class="comments"><a href="/blog/2014/02/04/arc/#comments">Comments</a></div>
</div>
</article>
<article class="post">
<h2 class="title">
<a href="/blog/2014/02/04/ios-viewcontroller-lifecycle/">
ViewController生命周期</a>
</h2>
<div class="entry-content">
<p>编程实现ViewController初始化时,创建一个自定义的初始化方法,调用父类的init方法,然后进行类的特定初始化。一般情况下,初始化方法不写过于复杂的。</p>
<p>ViewController加载View对象的过程:</p>
<ol>
<li>ViewController调用<code>loadView</code>方法,loadView的默认实现执行以下两个方法之一
<ul>
<li>如果ViewController与storyboard关联,它会从storyboard加载</li>
<li>如果ViewController不与storyboard关联,则会创建一个空UIView对象分配给View属性</li>
</ul>
</li>
<li>ViewController调用<code>viewDidLoad</code>方法,执行子类的额外加载任务</li>
</ol>
<p><img src="https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/Art/loading_a_view_into_memory_2x.png" alt="Drawing" width="600px"/></p>
<p>编程创建View,而不使用storyboard,应该重载loadView方法。在方法中实现以下步骤:</p>
<ol>
<li>创建root view对象
<ul>
<li>root view应该包含ViewController关联的所有其他view。一般情况下,root view的大小应该充满整个屏幕,但也可以根据需要调整。<a href="https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/AdoptingaFull-ScreenLayout/AdoptingaFull-ScreenLayout.html#//apple_ref/doc/uid/TP40007457-CH13-SW2">“Resizing the View Controller’s Views.”</a></li>
<li>你可以使用通用的UIView对象,也可以使用自定义的UIView,以及任何view的扩展来填充屏幕</li>
</ul>
</li>
<li>创建subviews添加到root view
<ul>
<li>对于每个subview都应该创建并初始化</li>
<li>添加到父view使用<code>addSubview:</code>方法</li>
</ul>
</li>
<li>如果你使用auto layout,对于你创建的每个view设置足够的约束,否则,实现<code>viewWillLayoutSubviews</code>和<code>viewDidLayoutSubviews</code>来调整subview的大小</li>
<li>分配root view给ViewController的view属性</li>
</ol>
<p>编程创建view示例</p>
<pre><code>- (void)loadView
{
CGRect applicationFrame = [[UIScreen mainScreen] applicationFrame];
UIView *contentView = [[UIView alloc] initWithFrame:applicationFrame];
contentView.backgroundColor = [UIColor blackColor];
self.view = contentView;
levelView = [[LevelView alloc] initWithFrame:applicationFrame viewController:self];
[self.view addSubview:levelView];
}
</code></pre>
<p>有效的内存管理:</p>
<ul>
<li><code>Initialization methods</code> 为ViewController分配关键的数据结构</li>
<li><code>loadView</code> 创建你的view对象,如果使用storyboard,则不需要重载此方法</li>
<li><code>Custom properties and methods</code> 创建自定义对象</li>
<li><code>viewDidLoad</code> 分配或者加载view所需数据</li>
<li><code>didReceiveMemoryWarning</code> 使用这个方法来释放所有ViewController相关联的非必要对象,ios6以后,也可以在这个方法释放view</li>
<li><code>dealloc</code> 重载这个方法只执行ViewController的最后清理,实例变量和属性对象被自动释放,不需要显式地释放它们</li>
</ul>
<p>对于ios6以后的版本,按需要释放ViewController的view对象。对于释放view对象来获取内存空间的必要性,应该由开发者来判断。</p>
<p>释放ViewController不显示的view</p>
<pre><code>- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Add code to clean up any of your own resources that are no longer necessary.
if ([self.view window] == nil)
{
// Add code to preserve data stored in the views that might be
// needed later.
// Add code to clean up other strong references to the view in
// the view hierarchy.
self.view = nil;
}
}
</code></pre>
<p>整个ViewController的生命周期中,其余方法调用如图所示</p>
<p><img src="https://7ms8tg.bn1302.livefilestore.com/y2pZcyHRGXeoNc8f_xIc_p6jiPs2MfjwZDTauC3kpvuiVMizKjjw7juJE8YxZ8Dbwc_Pllati10dX4qjtHAEapplA1sRjUrct7neTZkbnRKTCg/BCCDAEFE-C8E6-4CE5-B72D-6B2F76CA8544.png?psid=1" alt="" /></p>
</div>
<div class="meta">
<div class="date">
<time datetime="2014-02-04T20:38:21+08:00" pubdate data-updated="true">Feb 4<span>th</span>, 2014</time></div>
<div class="tags">
<a class='category' href='/blog/categories/ios/'>ios</a>
</div>
<div class="comments"><a href="/blog/2014/02/04/ios-viewcontroller-lifecycle/#comments">Comments</a></div>
</div>
</article>
<article class="post">
<h2 class="title">
<a href="/blog/2014/02/04/delegate-messaging/">
应用生命周期</a>
</h2>
<div class="entry-content">
<h4>ios应用有5种状态:</h4>
<ul>
<li>Not Running(非运行状态)应用未运行</li>
<li>Inactive(前台非活动状态)应用正在进入前台,此时不接受事件处理</li>
<li>Active(前台活动状态)前台正常运行状态</li>
<li>Background(后台状态)不存在后台run loop,则进入Suspended状态</li>
<li>Suspended(挂起状态)不执行代码,内存不够时,应用将终止</li>
</ul>
<h4>整个应用的生命周期如图所示</h4>
<p><img src="http://www.cocoanetics.com/files/Bildschirmfoto-2012-03-05-um-5.26.29-PM.png" alt="Drawing" width="600px"/></p>
<h4>总结:</h4>
<ul>
<li><code>didFinishLaunching</code>整个生命周期只会调用一次。</li>
<li>应用能进行后台运行,首先SDK必须在4.0以上的版本,其次得在info.plist中不禁用后台。</li>
<li>内存不足情况下,以及用户自行关闭应用的情况下,不会执行<code>applicationWillTerminate:</code>,所以必须要在<code>applicationWillResignActive</code>事件里保存数据。</li>
<li><code>becomeActive</code>和<code>resignActive</code>配对操作进行UI数据等的恢复。</li>
<li><code>enterBackground</code>和<code>enterForeground</code>配对操作进行用户数据等的恢复。</li>
<li>在以前,当app被按home键退出后,app仅有最多5秒钟的时候做一些保存或清理资源的工作。但是应用可以调用UIApplication的<code>beginBackgroundTaskWithExpirationHandler</code>方法,让app最多有10分钟的时间在后台长久运行。这个时间可以用来做清理本地缓存,发送统计数据等工作。</li>
</ul>
</div>
<div class="meta">
<div class="date">
<time datetime="2014-02-04T17:58:47+08:00" pubdate data-updated="true">Feb 4<span>th</span>, 2014</time></div>
<div class="tags">
<a class='category' href='/blog/categories/ios/'>ios</a>
</div>
<div class="comments"><a href="/blog/2014/02/04/delegate-messaging/#comments">Comments</a></div>
</div>
</article>
<nav id="pagenavi">
<div class="center"><a href="/blog/archives">Blog Archives</a></div>
</nav></div>
<footer id="footer" class="inner">Copyright © 2014
yechunxiao19
</footer>
<script src="/javascripts/slash.js"></script>
<script src="/javascripts/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
(function($){
$('.fancybox').fancybox();
})(jQuery);
</script> <!-- Delete or comment this line to disable Fancybox -->
</body>
</html>