-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
257 lines (203 loc) · 150 KB
/
atom.xml
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>MengQi Yang</title>
<link href="/atom.xml" rel="self"/>
<link href="http://5mengqi.cc/"/>
<updated>2017-05-10T05:51:22.000Z</updated>
<id>http://5mengqi.cc/</id>
<author>
<name>MengQi Yang</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>聊聊中介者模式</title>
<link href="http://5mengqi.cc/blogs/talk-about-the-mediator-pattern/"/>
<id>http://5mengqi.cc/blogs/talk-about-the-mediator-pattern/</id>
<published>2017-05-11T05:51:47.000Z</published>
<updated>2017-05-10T05:51:22.000Z</updated>
<content type="html"><![CDATA[<h2 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h2><p>之所以想聊聊中介者模式,是因为在之前关于iOS组件化方案的争论中拜读了bang大神的总结,他文中详细的介绍了如何利用中介者模式来解决模块之间的调用依赖。</p>
<h2 id="聊聊中介者模式"><a href="#聊聊中介者模式" class="headerlink" title="聊聊中介者模式"></a>聊聊中介者模式</h2><p>中介者模式:用一个对象来封装一系列对象的交互方式。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。其类图如下:<br><img src="/images/mediator-pattern-class-diagram.png" alt=""></p>
<p>图中描述的关系有,抽象Mediator会被ConcreteMediator实现,抽象Colleague会被ConcreteColleague1和ConcreteColleague2实现,抽象Colleague含有对抽象Mediator实例的引用,ConcreteMediator含有对ConcreteColleague1实例和ConcreteColleague2实例的引用。如果应用程序中只需要一个中介者,有时抽象的Mediator可以省略,一种可能的对象图如下:<br><img src="/images/mediator-pattern-obj-diagram.png" alt=""><br>在以下情形,自然会考虑使用这一模式:</p>
<ul>
<li>对象间的交互虽定义明确然而非常复杂,导致一组对象彼此相互依赖而且难以理解;</li>
<li>因为对象引用了许多其他对象并与其通讯,导致对象难以复用;</li>
<li>想要定制一个分布在多个类中的逻辑或行为,又不想生成太多子类。</li>
</ul>
<p>中介者模式也可能带来弊端,便是中介者类过于庞大而难以维护。</p>
<h2 id="实践中介者模式"><a href="#实践中介者模式" class="headerlink" title="实践中介者模式"></a>实践中介者模式</h2><p>Demo地址是<a href="https://github.com/monkiyang/MediatorPatternGo" target="_blank" rel="external">https://github.com/monkiyang/MediatorPatternGo</a>,关键代码摘录如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// MKMediator.m</span></div><div class="line">...</div><div class="line">+ (<span class="built_in">UIViewController</span> *)viewControllerWithModuleClass:(<span class="built_in">NSString</span> *)moduleClass selector:(<span class="built_in">NSString</span> *)selector params:(<span class="built_in">NSDictionary</span> *)params {</div><div class="line"> <span class="keyword">if</span> (moduleClass.length == <span class="number">0</span> || selector.length == <span class="number">0</span> || !params) {</div><div class="line"> <span class="keyword">return</span> <span class="literal">nil</span>;</div><div class="line"> }</div><div class="line"> <span class="comment">//target-action解除MKMediator对MKModuleX的感官依赖,运行时仍存在依赖</span></div><div class="line"> Class cls = <span class="built_in">NSClassFromString</span>(moduleClass);</div><div class="line"> SEL sel = <span class="built_in">NSSelectorFromString</span>(selector);</div><div class="line"> <span class="keyword">if</span> (!cls || !sel) {</div><div class="line"> <span class="keyword">return</span> <span class="literal">nil</span>;</div><div class="line"> }</div><div class="line"> </div><div class="line"> <span class="keyword">if</span> ([cls respondsToSelector:sel]) {</div><div class="line"><span class="meta">#pragma clang diagnostic push</span></div><div class="line"><span class="meta">#pragma clang diagnostic ignored <span class="meta-string">"-Warc-performSelector-leaks"</span></span></div><div class="line"> <span class="keyword">return</span> [cls performSelector:sel withObject:params];</div><div class="line"><span class="meta">#pragma clang diagnostic pop</span></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> <span class="literal">nil</span>;</div><div class="line">}</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure></p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://www.amazon.cn/Objective-C%E7%BC%96%E7%A8%8B%E4%B9%8B%E9%81%93-iOS%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E8%A7%A3%E6%9E%90-%E9%92%9F%E5%86%A0%E8%B4%A4/dp/B0065V3ALO/ref=sr_1_1?s=books&ie=UTF8&qid=1494327326&sr=1-1&keywords=Objective-C%E7%BC%96%E7%A8%8B%E4%B9%8B%E9%81%93" target="_blank" rel="external">《Objective-C编程之道iOS设计模式解析》</a><br><a href="http://blog.cnbang.net/tech/3080/" target="_blank" rel="external">iOS 组件化方案探索</a> by <a href="http://blog.cnbang.net/" target="_blank" rel="external">bang</a></p>
]]></content>
<summary type="html">
之所以想聊聊中介者模式,是因为在之前关于iOS组件化方案的争论中拜读了bang大神的总结,他文中详细的介绍了如何利用中介者模式来解决模块之间的调用依赖。
</summary>
<category term="iOS" scheme="http://5mengqi.cc/categories/ios/"/>
<category term="中介者模式" scheme="http://5mengqi.cc/tags/%E4%B8%AD%E4%BB%8B%E8%80%85%E6%A8%A1%E5%BC%8F/"/>
</entry>
<entry>
<title>谈谈MVC和MVVM</title>
<link href="http://5mengqi.cc/blogs/talk-about-mvc-and-mvvm/"/>
<id>http://5mengqi.cc/blogs/talk-about-mvc-and-mvvm/</id>
<published>2017-05-02T02:12:24.000Z</published>
<updated>2017-05-02T02:12:25.000Z</updated>
<content type="html"><![CDATA[<h2 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h2><p>刚接触iOS这行时,对架构这些没什么概念,很多时候是为了实现功能,代码难免写得杂乱不堪,这5年积累让我认识到写代码简洁优雅、层次分明、思路清晰是非常重要的。</p>
<h2 id="谈谈MVC"><a href="#谈谈MVC" class="headerlink" title="谈谈MVC"></a>谈谈MVC</h2><p>从做iOS开发开始,接触最多的就是MVC了,我对MVC的掌握是从白胡子老头的讲课中学习来的。首先我们要明确各层的职责,Model(模型层)主要是指数据模型对象,例如Person类,View(视图层)主要是指由视图控件构成的界面,Controller(控制器层)主要负责数据操作、数据在界面上呈现。单个MVC的结构图如下:</p>
<p><img src="/images/single-mvc.png" alt=""></p>
<p>从图中我们可以看出各层之间的通信关系以及方式,控制器对象持有了视图对象和模型对象,可以直接访问被持有对象公开的属性和方法,模型对象与视图对象之间不可进行通信,需要一个控制器对象进行间接通信,视图对象并不持有控制器对象,可通过Target-Action来进行通信,当用户操作视图时会发送Action给对应控制器(Target)处理,当控制器需要同步了解视图正在发生的事件(Should、Will、Did)时,视图会以Delegate模式来告知其设为代理的控制器发生了什么变化,视图对象也不会持有数据对象,控制器对象可以将数据对象的属性赋值给视图对象的属性,也可以通过代理模式(DataSource)从设为代理的控制器中实现代理方法返回所需数据,数据对象不会持有视图对象和控制器对象,数据对象发生变化时,控制器对象如需了解,可以通过通知方式(Notification、KVO)来通信,视图对象也可以接收通知,但是违背MVC!(注意:Cell持有Model,也是违背MVC的!)多个MVC配合的结构图如下:</p>
<p><img src="/images/mutiple-mvcs.png" alt=""></p>
<p>从图中我们可以看出,一个MVC可以作为另一个MVC的子视图部分,某些数据对象之间会发生通信(持有),可能存在全局共享数据对象或者一个数据对象拆分成多个子数据对象来供子MVC使用。以下为多个MVC错乱配合的结构图(难以调试、无法模块化):</p>
<p><img src="/images/mutiple-mvcs-error.png" alt=""></p>
<h2 id="谈谈MVVM"><a href="#谈谈MVVM" class="headerlink" title="谈谈MVVM"></a>谈谈MVVM</h2><p>对于遵从MVC架构的复杂模块而言,控制器中代码会变得很臃肿,单元测试十分困难,为了解决这个问题,提出了一种新的架构MVVM,相比而言前者更为普及,更容易接受。单个MVVM的结构图如下:</p>
<p><img src="/images/mvvm.png" alt=""></p>
<p>从图中我们看出,MVVM架构将UIView和UIViewController统一归为View(视图层),UIView与UIViewController之间仍是MVC架构中的通信关系,View中的UIViewController会持有ViewModel,View中的UIView会跟ViewModel在UIViewController中利用KVO/ReactiveCocoa实现数据和用户行为绑定,当用户操作界面发起数据变化时,ViewModel会进行相应业务逻辑处理并将新数据更新到界面上,ViewModel持有Model,负责业务逻辑处理后Model数据的更新,Model与ViewModel之间也存在数据绑定,可在其中进行数据加工处理,当Model数据变化时ViewModel数据也要相应变化。</p>
<h2 id="RAC实践MVVM"><a href="#RAC实践MVVM" class="headerlink" title="RAC实践MVVM"></a>RAC实践MVVM</h2><p>Demo地址是<a href="https://github.com/monkiyang/ReactiveCocoaGo" target="_blank" rel="external">https://github.com/monkiyang/ReactiveCocoaGo</a>,关键代码摘录如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// MKMainViewController.m</span></div><div class="line">...</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)learnRACCommand {</div><div class="line"> <span class="comment">//点击登录按钮处理登录请求</span></div><div class="line"> <span class="comment">//RACCommand绑定UI响应事件</span></div><div class="line"> <span class="keyword">self</span>.loginView.loginButton.rac_command = <span class="keyword">self</span>.userViewModel.loginCommand;</div><div class="line"> </div><div class="line"> <span class="comment">//RAC()、RACObserve()绑定控件与ViewModel</span></div><div class="line"> RAC(<span class="keyword">self</span>.loginView.statusLabel, text) = RACObserve(_userViewModel, status);</div><div class="line"> RAC(<span class="keyword">self</span>.infoView.nicknameLabel, text) = RACObserve(_userViewModel, nickname);</div><div class="line"> RAC(<span class="keyword">self</span>.infoView.genderLabel, text) = RACObserve(_userViewModel, gender);</div><div class="line">}</div><div class="line"></div><div class="line">...</div><div class="line"><span class="comment">// MKUserViewModel.m</span></div><div class="line">...</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)bindModel {</div><div class="line"> <span class="comment">//RAC()、RACObserve()绑定ViewModel与Model</span></div><div class="line"> RAC(<span class="keyword">self</span>, nickname) = [RACObserve(_userModel, nickname) map:^<span class="keyword">id</span>(<span class="built_in">NSString</span> *value) {</div><div class="line"> <span class="keyword">if</span> (value.length == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">return</span> value;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"My nickname is %@, "</span>, value];</div><div class="line"> }];</div><div class="line"> RAC(<span class="keyword">self</span>, gender) = [RACObserve(_userModel, gender) map:^<span class="keyword">id</span>(<span class="built_in">NSString</span> *value) {</div><div class="line"> <span class="keyword">if</span> (value.length == <span class="number">0</span>) {</div><div class="line"> <span class="keyword">return</span> value;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> [<span class="built_in">NSString</span> stringWithFormat:<span class="string">@"I'm %@."</span>, value];</div><div class="line"> }];</div><div class="line">}</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure></p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="http://open.163.com/movie/2015/2/3/4/MAIKHN60A_MAIKJBI34.html" target="_blank" rel="external">斯坦福大学公开课:iOS 8开发-[第2集] Xcode 、Swift和MVC</a><br><a href="http://www.cocoachina.com/ios/20160108/14916.html" target="_blank" rel="external">iOS 架构模式-解密 MVC,MVP,MVVM以及VIPER架构(译文)</a></p>
]]></content>
<summary type="html">
刚接触iOS这行时,对架构这些没什么概念,很多时候是为了实现功能,代码难免写得杂乱不堪,这5年积累让我认识到写代码简洁优雅、层次分明、思路清晰是非常重要的。
</summary>
<category term="iOS" scheme="http://5mengqi.cc/categories/ios/"/>
<category term="MVC" scheme="http://5mengqi.cc/tags/MVC/"/>
<category term="MVVM" scheme="http://5mengqi.cc/tags/MVVM/"/>
<category term="ReactiveCocoa" scheme="http://5mengqi.cc/tags/ReactiveCocoa/"/>
</entry>
<entry>
<title>iOS面试题收录(二)</title>
<link href="http://5mengqi.cc/blogs/include-ios-interview-questions-2/"/>
<id>http://5mengqi.cc/blogs/include-ios-interview-questions-2/</id>
<published>2017-04-13T15:05:54.000Z</published>
<updated>2017-04-13T15:38:29.000Z</updated>
<content type="html"><![CDATA[<p>接着<a href="http://5mengqi.cc/blogs/include-ios-interview-questions-1/">iOS面试题收录(一)</a>继续刷面试题!</p>
<h4 id="21-objc-msgForward函数是做什么的,直接调用它将会发生什么?"><a href="#21-objc-msgForward函数是做什么的,直接调用它将会发生什么?" class="headerlink" title="21._objc_msgForward函数是做什么的,直接调用它将会发生什么?"></a>21._objc_msgForward函数是做什么的,直接调用它将会发生什么?</h4><p>_objc_msgForward是void函数指针,也是IMP函数指针(<code>typedef void (*IMP)(void /* id, SEL, ... */ );</code>),在消息传递过程中,如果最终没找到方法的IMP,并且在resolve阶段也没有提供一个方法实现,则会指定_objc_msgForward为方法的IMP去执行以实现完整的消息转发机制。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// objc-runtime-new.mm</span></div><div class="line">...</div><div class="line">IMP lookUpImpOrForward(Class cls, SEL sel, <span class="keyword">id</span> inst, </div><div class="line"> <span class="keyword">bool</span> initialize, <span class="keyword">bool</span> cache, <span class="keyword">bool</span> resolver)</div><div class="line">{</div><div class="line"> ...</div><div class="line"> imp = cache_getImp(cls, sel);</div><div class="line"> ...</div><div class="line"></div><div class="line"> <span class="comment">// No implementation found. Try method resolver once.</span></div><div class="line"></div><div class="line"> <span class="keyword">if</span> (resolver && !triedResolver) {</div><div class="line"> runtimeLock.unlockRead();</div><div class="line"> _class_resolveMethod(cls, sel, inst);</div><div class="line"> <span class="comment">// Don't cache the result; we don't hold the lock so it may have </span></div><div class="line"> <span class="comment">// changed already. Re-do the search from scratch instead.</span></div><div class="line"> triedResolver = <span class="literal">YES</span>;</div><div class="line"> <span class="keyword">goto</span> retry;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// No implementation found, and method resolver didn't help. </span></div><div class="line"> <span class="comment">// Use forwarding.</span></div><div class="line"></div><div class="line"> imp = (IMP)_objc_msgForward_impcache;</div><div class="line"> cache_fill(cls, sel, imp, inst);</div><div class="line"></div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>通过断点暂停程序执行并在lldb中输入<code>call (void)instrumentObjcMessageSends(YES)</code>命令,之后可在/tmp/msgSend-xxxx文件(可在终端输入<code>open /private/tmp</code>前往相应路径)中查看运行时发送的所有消息,包括了消息转发中的相关方法。以下为实测结果:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// main.m</span></div><div class="line">...</div><div class="line"><span class="meta">#import <span class="meta-string">"Test.h"</span></span></div><div class="line"></div><div class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[]) {</div><div class="line"> <span class="keyword">@autoreleasepool</span> {</div><div class="line"> Test *test = [[Test alloc] init];</div><div class="line"> [test performSelector:<span class="keyword">@selector</span>(monkiyang)];<span class="comment">// 此处打断点</span></div><div class="line"> <span class="keyword">return</span> <span class="built_in">UIApplicationMain</span>(argc, argv, <span class="literal">nil</span>, <span class="built_in">NSStringFromClass</span>([AppDelegate <span class="keyword">class</span>]));</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// lldb</span></div><div class="line"><span class="meta"># -[Test monkiyang]: unrecognized selector sent to instance 0x600000000ee0</span></div><div class="line"><span class="meta"># *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Test monkiyang]: unrecognized selector sent to instance 0x600000000ee0'</span></div><div class="line"><span class="meta"># *** First throw call stack:</span></div><div class="line"><span class="meta"># (</span></div><div class="line"><span class="meta"># 0 CoreFoundation 0x0000000109a07b0b __exceptionPreprocess + 171</span></div><div class="line"><span class="meta"># 1 libobjc.A.dylib 0x00000001090d3141 objc_exception_throw + 48</span></div><div class="line"><span class="meta"># 2 CoreFoundation 0x0000000109a77134 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132</span></div><div class="line"><span class="meta"># 3 CoreFoundation 0x000000010998e840 ___forwarding___ + 1024</span></div><div class="line"><span class="meta"># 4 CoreFoundation 0x000000010998e3b8 _CF_forwarding_prep_0 + 120</span></div><div class="line"><span class="meta"># 5 test 0x00000001080fc7b5 main + 101</span></div><div class="line"><span class="meta"># 6 libdyld.dylib 0x000000010cfc665d start + 1</span></div><div class="line"><span class="meta"># 7 ??? 0x0000000000000001 0x0 + 1</span></div><div class="line"><span class="meta"># )</span></div><div class="line"><span class="meta"># libc++abi.dylib: terminating with uncaught exception of type NSException</span></div><div class="line"><span class="meta"># (lldb) call (void)instrumentObjcMessageSends(YES)</span></div><div class="line"></div><div class="line"><span class="comment">// msgSends-9816</span></div><div class="line">+ Test <span class="built_in">NSObject</span> initialize</div><div class="line">+ Test <span class="built_in">NSObject</span> alloc</div><div class="line">- Test <span class="built_in">NSObject</span> init</div><div class="line">- Test <span class="built_in">NSObject</span> performSelector:</div><div class="line">+ Test <span class="built_in">NSObject</span> resolveInstanceMethod:</div><div class="line">+ Test <span class="built_in">NSObject</span> resolveInstanceMethod:</div><div class="line">- Test <span class="built_in">NSObject</span> forwardingTargetForSelector:</div><div class="line">- Test <span class="built_in">NSObject</span> forwardingTargetForSelector:</div><div class="line">- Test <span class="built_in">NSObject</span> methodSignatureForSelector:</div><div class="line">- Test <span class="built_in">NSObject</span> methodSignatureForSelector:</div><div class="line">- Test <span class="built_in">NSObject</span> <span class="keyword">class</span></div><div class="line">- Test <span class="built_in">NSObject</span> doesNotRecognizeSelector:</div><div class="line">- Test <span class="built_in">NSObject</span> doesNotRecognizeSelector:</div><div class="line">- Test <span class="built_in">NSObject</span> <span class="keyword">class</span></div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>直接调用_objc_msgForward是非常危险的事情,用不好的话程序就会崩溃。因为直接调用_objc_msgForward,会跳过查找IMP的过程,触发消息转发,而消息转发没实现的话最终就会导致doesNotRecognizeSelector异常。_objc_msgForward函数的参数包括id receciver、SEL sel和可选参数,使用案例有<a href="https://github.com/bang590/JSPatch" target="_blank" rel="external">JSPatch</a>和<a href="https://github.com/ReactiveCocoa/ReactiveCocoa" target="_blank" rel="external">ReactiveCocoa</a>。</p>
<h4 id="22-runloop和线程有什么关系?"><a href="#22-runloop和线程有什么关系?" class="headerlink" title="22.runloop和线程有什么关系?"></a>22.runloop和线程有什么关系?</h4><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// CFRunLoop.c</span></div><div class="line">...</div><div class="line"><span class="keyword">static</span> <span class="built_in">CFMutableDictionaryRef</span> __CFRunLoops = <span class="literal">NULL</span>;</div><div class="line"><span class="keyword">static</span> <span class="built_in">CFLock_t</span> loopsLock = <span class="built_in">CFLockInit</span>;</div><div class="line"></div><div class="line"><span class="comment">// should only be called by Foundation</span></div><div class="line"><span class="comment">// t==0 is a synonym for "main thread" that always works</span></div><div class="line"><span class="comment">//根据线程取RunLoop</span></div><div class="line"><span class="built_in">CF_EXPORT</span> <span class="built_in">CFRunLoopRef</span> _CFRunLoopGet0(pthread_t t) {</div><div class="line"> <span class="keyword">if</span> (pthread_equal(t, kNilPthreadT)) {</div><div class="line"> t = pthread_main_thread_np();</div><div class="line"> }</div><div class="line"> __CFLock(&loopsLock);</div><div class="line"> <span class="comment">//如果存储RunLoop的字典不存在</span></div><div class="line"> <span class="keyword">if</span> (!__CFRunLoops) {</div><div class="line"> __CFUnlock(&loopsLock);</div><div class="line"> <span class="comment">//创建一个临时字典dict</span></div><div class="line"> <span class="built_in">CFMutableDictionaryRef</span> dict = <span class="built_in">CFDictionaryCreateMutable</span>(kCFAllocatorSystemDefault, <span class="number">0</span>, <span class="literal">NULL</span>, &kCFTypeDictionaryValueCallBacks);</div><div class="line"> <span class="comment">//创建主线程的RunLoop</span></div><div class="line"> <span class="built_in">CFRunLoopRef</span> mainLoop = __CFRunLoopCreate(pthread_main_thread_np());</div><div class="line"> <span class="comment">//把主线程的RunLoop保存到dict中,key是线程,value是RunLoop</span></div><div class="line"> <span class="built_in">CFDictionarySetValue</span>(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);</div><div class="line"> <span class="comment">//此处NULL和__CFRunLoops指针都指向NULL,匹配,所以将dict写到__CFRunLoops</span></div><div class="line"> <span class="keyword">if</span> (!OSAtomicCompareAndSwapPtrBarrier(<span class="literal">NULL</span>, dict, (<span class="keyword">void</span> * <span class="keyword">volatile</span> *)&__CFRunLoops)) {</div><div class="line"> <span class="comment">//释放dict</span></div><div class="line"> <span class="built_in">CFRelease</span>(dict);</div><div class="line"> }</div><div class="line"> <span class="comment">//释放mainrunloop</span></div><div class="line"> <span class="built_in">CFRelease</span>(mainLoop);</div><div class="line"> __CFLock(&loopsLock);</div><div class="line"> }</div><div class="line"> <span class="comment">//以上说明,第一次进来的时候,不管是getMainRunloop还是get子线程的runloop,主线程的runloop总是会被创建</span></div><div class="line"> <span class="comment">//从字典__CFRunLoops中获取传入线程t的runloop</span></div><div class="line"> <span class="built_in">CFRunLoopRef</span> loop = (<span class="built_in">CFRunLoopRef</span>)<span class="built_in">CFDictionaryGetValue</span>(__CFRunLoops, pthreadPointer(t));</div><div class="line"> __CFUnlock(&loopsLock);</div><div class="line"> <span class="comment">//如果没有获取到</span></div><div class="line"> <span class="keyword">if</span> (!loop) {</div><div class="line"> <span class="comment">//根据线程t创建一个runloop</span></div><div class="line"> <span class="built_in">CFRunLoopRef</span> newLoop = __CFRunLoopCreate(t);</div><div class="line"> __CFLock(&loopsLock);</div><div class="line"> <span class="comment">//把newLoop存入字典__CFRunLoops,key是线程t</span></div><div class="line"> loop = (<span class="built_in">CFRunLoopRef</span>)<span class="built_in">CFDictionaryGetValue</span>(__CFRunLoops, pthreadPointer(t));</div><div class="line"> <span class="keyword">if</span> (!loop) {</div><div class="line"> <span class="built_in">CFDictionarySetValue</span>(__CFRunLoops, pthreadPointer(t), newLoop);</div><div class="line"> loop = newLoop;</div><div class="line"> }</div><div class="line"> <span class="comment">// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it</span></div><div class="line"> __CFUnlock(&loopsLock);</div><div class="line"> <span class="built_in">CFRelease</span>(newLoop);</div><div class="line"> }</div><div class="line"> <span class="comment">//如果传入线程就是当前线程</span></div><div class="line"> <span class="keyword">if</span> (pthread_equal(t, pthread_self())) {</div><div class="line"> _CFSetTSD(__CFTSDKeyRunLoop, (<span class="keyword">void</span> *)loop, <span class="literal">NULL</span>);</div><div class="line"> <span class="keyword">if</span> (<span class="number">0</span> == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {</div><div class="line"> <span class="comment">//注册一个回调,当线程销毁时,销毁对应的RunLoop</span></div><div class="line"> _CFSetTSD(__CFTSDKeyRunLoopCntr, (<span class="keyword">void</span> *)(PTHREAD_DESTRUCTOR_ITERATIONS<span class="number">-1</span>), (<span class="keyword">void</span> (*)(<span class="keyword">void</span> *))__CFFinalizeRunLoop);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> loop;</div><div class="line">}</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure>
<p>根据从CFRunloop.c中摘录的代码看出,Runloop和线程是一一对应的,对应方式为键值对(线程:Runnloop)方式保存在一个全局字典中;主线程的Runnloop会在初始化全局字典时创建;子线程的Runloop会在首次获取不到时创建;Runloop会在线程销毁时销毁。</p>
<h4 id="23-runloop的mode作用是什么?"><a href="#23-runloop的mode作用是什么?" class="headerlink" title="23.runloop的mode作用是什么?"></a>23.runloop的mode作用是什么?</h4><p>RunLoop中的mode包含了一个name,若干source0、source1、timer、observer和port,在RunLoop运行过程中只有某一个mode会被处理,因此mode会指定这些事件在RunLoop中的优先级。Cocoa和Core Foundation框架定义了一些标准mode,如下:<br>NSDefaultRunLoopMode(Cocoa)/kCFRunLoopDefaultMode(Core Foundation),默认mode,主线程的RunLoop默认在该mode下运行;<br>GSEventReceiveRunLoopMode(Cocoa),接收系统内部事件;<br>UIInitializationRunLoopMode(Cocoa),程序初始化时运行在该mode下;<br>UITrackingRunLoopMode(Cocoa),追踪触摸手势,确保界面刷新不会卡顿,滑动tableview、scrollview等都运行在该mode下;<br>NSRunLoopCommonModes(Cocoa)/kCFRunLoopCommonModes(Core Foundation),标记为common的mode集合。(可指定name和添加事件来自定义一个mode添加至_commonModes)</p>
<h4 id="24-以-scheduledTimerWithTimeInterval…的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?"><a href="#24-以-scheduledTimerWithTimeInterval…的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?" class="headerlink" title="24.以+ scheduledTimerWithTimeInterval…的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?"></a>24.以+ scheduledTimerWithTimeInterval…的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?</h4><p>因为RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下并重启一个新的。在这个机制下,当scrollview滚动时NSDefaultRunLoopMode(kCFRunLoopDefaultMode)会切换到UITrackingRunLoopMode来保证scrollview的流畅滑动。以+scheduledTimerWithTimeInterval…方式触发的timer是以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主RunLoop中的,所以在滑动页面上的列表时,timer会暂停回调。解决方案是将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)中,代码如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">NSTimer</span> *timer = [<span class="built_in">NSTimer</span> timerWithTimeInterval:<span class="number">1.0</span> target:<span class="keyword">self</span> selector:<span class="keyword">@selector</span>(monkiyang:) userInfo:<span class="literal">nil</span> repeats:<span class="literal">YES</span>];</div><div class="line">[[<span class="built_in">NSRunLoop</span> currentRunLoop] addTimer:timer forMode:<span class="built_in">NSRunLoopCommonModes</span>];</div></pre></td></tr></table></figure></p>
<h4 id="25-猜想runloop内部是如何实现的?"><a href="#25-猜想runloop内部是如何实现的?" class="headerlink" title="25.猜想runloop内部是如何实现的?"></a>25.猜想runloop内部是如何实现的?</h4><p>一般来说,一个线程一次只能执行一个任务,执行完成后线程就会退出。如果我们需要一个机制,让线程能随时处理事情但并不退出,通常的代码逻辑是这样的:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">function loop() {</div><div class="line"> initialize();</div><div class="line"> do {</div><div class="line"> var message = get_next_message();</div><div class="line"> process_message(message);</div><div class="line"> } while (message != quit);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h4 id="26-objc使用什么机制管理对象内存?"><a href="#26-objc使用什么机制管理对象内存?" class="headerlink" title="26.objc使用什么机制管理对象内存?"></a>26.objc使用什么机制管理对象内存?</h4><p>通过retainCount(引用计数)机制来决定对象是否需要释放。每次RunLoop时,都会检查对象的retainCount,如果retainCount为0,说明该对象没有地方需要继续使用了,可以释放掉。</p>
<h4 id="27-ARC通过什么方式帮助开发者管理内存?"><a href="#27-ARC通过什么方式帮助开发者管理内存?" class="headerlink" title="27.ARC通过什么方式帮助开发者管理内存?"></a>27.ARC通过什么方式帮助开发者管理内存?</h4><p>ARC是基于MRC的,会在编译期间通过插入对应的内存管理代码(如retain、release等)来管理对象的内存分配和释放。</p>
<h4 id="28-不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)"><a href="#28-不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)" class="headerlink" title="28.不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)"></a>28.不手动指定autoreleasepool的前提下,一个autorealese对象在什么时刻释放?(比如在一个vc的viewDidLoad中创建)</h4><p>一个对象发送autorelease消息,就是将这个对象加入到当前的自动释放池(实质是由若干个AutoreleasePoolPage对象以双向链表的形式组合而成)中,在不指定@autoreleasepool{}的前提下,这个对象会在当前RunLoop迭代结束时释放,因为系统在每个RunLoop迭代中都加入了自动释放池Push和Pop。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">void</span> *context = objc_autoreleasePoolPush();<span class="comment">// 哨兵对象(0,即nil)地址</span></div><div class="line"><span class="comment">// {}中的代码</span></div><div class="line">objc_autoreleasePoolPop(context);</div></pre></td></tr></table></figure></p>
<h4 id="29-BAD-ACCESS在什么情况下出现?"><a href="#29-BAD-ACCESS在什么情况下出现?" class="headerlink" title="29.BAD_ACCESS在什么情况下出现?"></a>29.BAD_ACCESS在什么情况下出现?</h4><p>访问野指针(对一个已经释放的对象发消息),死循环。</p>
<h4 id="30-苹果是如何实现autoreleasepool的?"><a href="#30-苹果是如何实现autoreleasepool的?" class="headerlink" title="30.苹果是如何实现autoreleasepool的?"></a>30.苹果是如何实现autoreleasepool的?</h4><p>AutoreleasePool实质是由若干个AutoreleasePoolPage以双向链表的形式组织而成,对应着每个AutoreleasePoolPage中都包含一个parent指针和child指针,其内部的thread指针指向当前线程,每个AutoreleasePoolPage对象会开辟4096字节内存(也就是虚拟内存一页的大小),除了实例变量所占空间,剩余空间全部用来存储autorelease对象的地址,其内部的next指针作为游标指向栈顶最新add进来的autorelease对象的下一个位置,如果一个AutoreleasePoolPage的空间被占满,会新建一个AutoreleasePoolPage对象,连接链表,后来的autorelease对象会加入新的page中。如果一个对象发送autorelease消息,就是将这个对象加入到当前AutoreleasePoolPage的栈顶next指针指向的位置。每当调用一次objc_autoreleasePoolPush时,会向当前的AutoreleasePoolPage中加入一个哨兵对象(0,即nil),此函数返回值就是这个哨兵对象的地址,被objc_autoreleasePoolPop(哨兵对象)作为入参,于是根据传入的哨兵对象地址找到所处的page,将晚于哨兵对象插入的所有autorelease对象发送release消息,并向栈底移动next指针,可跨越多个page,直到哨兵所处的page。</p>
<h4 id="31-使用block时什么情况会发生引用循环,如何解决?"><a href="#31-使用block时什么情况会发生引用循环,如何解决?" class="headerlink" title="31.使用block时什么情况会发生引用循环,如何解决?"></a>31.使用block时什么情况会发生引用循环,如何解决?</h4><p>当一个对象强引用了block,而在block中又强引用了该对象,就会发生循环引用。通过使用<strong>weak修饰这个对象(</strong>weak typeof(self) weakSelf = self)或者将其中一方强制置空(比如在viewDidAppear中将self.block = nil)。</p>
<h4 id="32-在block内如何修改block外部变量?"><a href="#32-在block内如何修改block外部变量?" class="headerlink" title="32.在block内如何修改block外部变量?"></a>32.在block内如何修改block外部变量?</h4><p>使用_block修饰可实现修改block外部变量,_block所起的作用是只要观察到该变量被block所持有,就将外部变量(纯量/指针类型)在栈中的内存地址放到了堆中,进而在block内部也可以修改外部变量的值。验证如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">__block <span class="keyword">int</span> a = <span class="number">0</span>;</div><div class="line"><span class="built_in">NSLog</span>(<span class="string">@"定义前:%p"</span>, &a); <span class="comment">//栈区</span></div><div class="line"><span class="keyword">void</span> (^foo)(<span class="keyword">void</span>) = ^{</div><div class="line"> a = <span class="number">1</span>;</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"block内部:%p"</span>, &a); <span class="comment">//堆区</span></div><div class="line">};</div><div class="line"><span class="built_in">NSLog</span>(<span class="string">@"定义后:%p"</span>, &a); <span class="comment">//堆区</span></div><div class="line">foo();</div><div class="line"></div><div class="line"><span class="meta"># 定义前:0x7fff51b7e078</span></div><div class="line"><span class="meta"># 定义后:0x60800002f3b8</span></div><div class="line"><span class="meta"># block内部:0x60800002f3b8</span></div></pre></td></tr></table></figure></p>
<h4 id="34-使用系统的某些block-api(如UIView的block版本写动画时),是否也考虑引用循环问题?"><a href="#34-使用系统的某些block-api(如UIView的block版本写动画时),是否也考虑引用循环问题?" class="headerlink" title="34.使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?"></a>34.使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?</h4><p>所谓引用循环是指双向的强引用,所以那些单向的强引用(block强引用self)是没有问题的,比如UIView的block版本写动画<code>[UIView animateWithDuration:duration animations:^{[self.superview layoutIfNeeded]}]</code>等等,但如果你使用一些参数中可能含有实例变量的系统api或者返回值赋给实例变量时,需要考虑引用循环:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//场景1</span></div><div class="line">__<span class="keyword">weak</span> <span class="keyword">typeof</span>(<span class="keyword">self</span>) weakSelf = <span class="keyword">self</span>;</div><div class="line">dispatch_group_async(_operationGroup, _operationsQueue, ^ {</div><div class="line"> __<span class="keyword">strong</span> <span class="keyword">typeof</span>(weakSelf) strongSelf = weakSelf;</div><div class="line"> [strongSelf dosSomething];</div><div class="line">});</div><div class="line"></div><div class="line"><span class="comment">//场景2</span></div><div class="line">__<span class="keyword">weak</span> <span class="keyword">typeof</span>(<span class="keyword">self</span>) weakSelf = <span class="keyword">self</span>;</div><div class="line">_observer = [[<span class="built_in">NSNotificationCenter</span> defaultCenter] addObserverForName:<span class="string">@"monkiyang"</span> object:<span class="literal">nil</span> queue:<span class="literal">nil</span> usingBlock:^(<span class="built_in">NSNotification</span> *notification) {</div><div class="line"> __<span class="keyword">strong</span> <span class="keyword">typeof</span>(weakSelf) strongSelf = <span class="keyword">self</span>;</div><div class="line"> [strongSelf doSomething];</div><div class="line">}];</div></pre></td></tr></table></figure></p>
<h4 id="35-GCD的队列(dispatch-queue-t)分哪两种类型?"><a href="#35-GCD的队列(dispatch-queue-t)分哪两种类型?" class="headerlink" title="35.GCD的队列(dispatch_queue_t)分哪两种类型?"></a>35.GCD的队列(dispatch_queue_t)分哪两种类型?</h4><p>串行队列(Serial Dispatch Queue)和并行队列(Concurrent Dispatch Queue)</p>
<h4 id="36-如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)"><a href="#36-如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)" class="headerlink" title="36.如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)"></a>36.如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)</h4><p>使用dispatch_group_async追加block到并行队列中,全部执行完毕通过dispatch_group_notify在其block中结束处理。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">dispatch_queue_t</span> queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, <span class="number">0</span>);</div><div class="line">dispatch_group_t group = dispatch_group_create();</div><div class="line">dispatch_group_async(group, queue, ^{<span class="comment">// <span class="doctag">TODO:</span> 加载图片1});</span></div><div class="line">dispatch_group_async(group, queue, ^{<span class="comment">// <span class="doctag">TODO:</span> 加载图片2});</span></div><div class="line">dispatch_group_async(group, queue, ^{<span class="comment">// <span class="doctag">TODO:</span> 加载图片3});</span></div><div class="line">dispatch_group_notify(group, dispatch_get_main_queue(), ^{<span class="comment">// <span class="doctag">TODO:</span> 合并图片});</span></div></pre></td></tr></table></figure></p>
<h4 id="37-dispatch-barrier-async的作用是什么?"><a href="#37-dispatch-barrier-async的作用是什么?" class="headerlink" title="37.dispatch_barrier_async的作用是什么?"></a>37.dispatch_barrier_async的作用是什么?</h4><p>dispatch_barrier_async会等待之前追加到并行队列中的操作全部执行完毕之后,再执行dispatch_barrier_async追加的处理,等dispatch_barrier_async追加的处理执行结束之后,才恢复队列中之后操作的并行处理。(注意:使用dispatch_barrier_async,只能搭配自定义并行队列dipatch_queue_t,不能通过dispatch_get_global_queue获取,否则dispatch_barrier_async不起作用,追加的操作一样并行处理)</p>
<h4 id="38-苹果为什么要废弃dispatch-get-current-queue?"><a href="#38-苹果为什么要废弃dispatch-get-current-queue?" class="headerlink" title="38.苹果为什么要废弃dispatch_get_current_queue?"></a>38.苹果为什么要废弃dispatch_get_current_queue?</h4><p>dispatch_get_current_queue并不能解决死锁问题,场景如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">dispatch_queue_t</span> queueA = dispatch_queue_create(<span class="string">"com.monkiyang.queueA"</span>, <span class="literal">NULL</span>);</div><div class="line"><span class="built_in">dispatch_queue_t</span> queueB = dispatch_queue_create(<span class="string">"com.monkiyang.queueB"</span>, <span class="literal">NULL</span>);</div><div class="line"></div><div class="line"><span class="built_in">dispatch_sync</span>(queueA, ^{</div><div class="line"> <span class="built_in">dispatch_sync</span>(queueB, ^{</div><div class="line"> dispatch_block_t block = ^{ <span class="comment">/* ... */</span> };</div><div class="line"> <span class="comment">// queueB != queueA</span></div><div class="line"> <span class="keyword">if</span> (dispatch_get_current_queue() == queueA) {</div><div class="line"> block();</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="comment">// 死锁</span></div><div class="line"> <span class="built_in">dispatch_sync</span>(queueA, block);</div><div class="line"> }</div><div class="line"> })</div><div class="line">})</div></pre></td></tr></table></figure></p>
<h4 id="39-以下代码运行结果如何?"><a href="#39-以下代码运行结果如何?" class="headerlink" title="39.以下代码运行结果如何?"></a>39.以下代码运行结果如何?</h4><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">- (<span class="keyword">void</span>)viewDidLoad</div><div class="line">{</div><div class="line"> [<span class="keyword">super</span> viewDidLoad];</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"1"</span>);</div><div class="line"> <span class="built_in">dispatch_sync</span>(dispatch_get_main_queue(), ^{</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"2"</span>);</div><div class="line"> });</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"3"</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>主线程死锁。</p>
<h4 id="40-addObserver-forKeyPath-options-context-各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?"><a href="#40-addObserver-forKeyPath-options-context-各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?" class="headerlink" title="40.addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?"></a>40.addObserver:forKeyPath:options:context:各个参数的作用分别是什么,observer中需要实现哪个方法才能获得KVO回调?</h4><p>各个参数分别是观察者、观察的属性、观察的选项、上下文,回调方法是<code>- (void)observerValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context</code></p>
<h4 id="41-如何手动触发一个value的KVO"><a href="#41-如何手动触发一个value的KVO" class="headerlink" title="41.如何手动触发一个value的KVO"></a>41.如何手动触发一个value的KVO</h4><p>键值观察通知(KVO)依赖于NSObject的两个方法:<code>willChangeValueForKey:</code>和<code>didChangeValueForKey:</code>,在一个被观察属性发生改变之前,<code>willChangeValueForKey:</code>会被调用并记录了旧值,而当改变发生后,<code>observeValueForKey:ofObject:change:context:</code>会被调用,继而调用<code>didChangeValueForKey:</code>。如果实现了这些调用,便可手动触发一个value的KVO,代码如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">...</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *test;</div><div class="line">...</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewDidLoad {</div><div class="line"> [<span class="keyword">super</span> viewDidLoad];</div><div class="line"></div><div class="line"> _test = <span class="string">@"test"</span>;</div><div class="line"> [<span class="keyword">self</span> addObserver:<span class="keyword">self</span> forKeyPath:<span class="string">@"test"</span> options:<span class="built_in">NSKeyValueObservingOptionOld</span> | <span class="built_in">NSKeyValueObservingOptionNew</span> context:<span class="literal">nil</span>];</div><div class="line"> </div><div class="line"> <span class="comment">//will..., did...必须成对存在</span></div><div class="line"> [<span class="keyword">self</span> willChangeValueForKey:<span class="string">@"test"</span>];</div><div class="line"> _test = <span class="string">@"hello monkiyang"</span>;</div><div class="line"> [<span class="keyword">self</span> didChangeValueForKey:<span class="string">@"test"</span>];</div><div class="line"> </div><div class="line"> _test = <span class="string">@"test"</span>;</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)observeValueForKeyPath:(<span class="built_in">NSString</span> *)keyPath ofObject:(<span class="keyword">id</span>)object change:(<span class="built_in">NSDictionary</span><<span class="built_in">NSKeyValueChangeKey</span>,<span class="keyword">id</span>> *)change context:(<span class="keyword">void</span> *)context {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"keyPath=%@, object=%@, change.old=%@, change.new=%@"</span>, keyPath, object, change[<span class="built_in">NSKeyValueChangeOldKey</span>], change[<span class="built_in">NSKeyValueChangeNewKey</span>]);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h4 id="42-若一个类有实例变量-NSString-foo-,调用setValue-forKey-时,可以以foo还是-foo-作为key?"><a href="#42-若一个类有实例变量-NSString-foo-,调用setValue-forKey-时,可以以foo还是-foo-作为key?" class="headerlink" title="42.若一个类有实例变量 NSString *_foo ,调用setValue:forKey:时,可以以foo还是 _foo 作为key?"></a>42.若一个类有实例变量 NSString *_foo ,调用setValue:forKey:时,可以以foo还是 _foo 作为key?</h4><p>都可以。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">ViewController</span>() </span>{</div><div class="line"> <span class="built_in">NSString</span> *_foo;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">ViewController</span></span></div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)viewDidLoad {</div><div class="line"> [<span class="keyword">super</span> viewDidLoad];</div><div class="line"></div><div class="line"> [<span class="keyword">self</span> setValue:<span class="string">@"monkiyang"</span> forKey:<span class="string">@"foo"</span>];</div><div class="line"> [<span class="keyword">self</span> setValue:<span class="string">@"test"</span> forKey:<span class="string">@"_foo"</span>];</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></p>
<h4 id="43-KVC的keyPath中的集合运算符如何使用?"><a href="#43-KVC的keyPath中的集合运算符如何使用?" class="headerlink" title="43.KVC的keyPath中的集合运算符如何使用?"></a>43.KVC的keyPath中的集合运算符如何使用?</h4><p>必须用在集合对象上或普通对象的集合属性上,简单集合运算符包括@avg、@count、@max、@min、@sum,格式为@”@sum.age”或@“集合属性[email protected]”。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 自定义Person类,包括一个NSInteger类型的age</span></div><div class="line">- (<span class="keyword">void</span>)viewDidLoad {</div><div class="line"> Person *person = [[Person alloc] init];</div><div class="line"> person.age = <span class="number">18</span>;</div><div class="line"> Person *person1 = [[Person alloc] init];</div><div class="line"> person1.age = <span class="number">20</span>;</div><div class="line"> Person *person2 = [[Person alloc] init];</div><div class="line"> person2.age = <span class="number">22</span>;</div><div class="line"> <span class="keyword">self</span>.persons = @[person, person1, person2];</div><div class="line"> </div><div class="line"> <span class="built_in">NSInteger</span> sum = [[_persons valueForKeyPath:<span class="string">@"@sum.age"</span>] integerValue];</div><div class="line"> <span class="built_in">NSInteger</span> avg = [[_persons valueForKeyPath:<span class="string">@"@avg.age"</span>] integerValue];</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"sum=%ld, avg=%ld"</span>, sum, avg);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h4 id="44-KVC和KVO的keyPath一定是属性么?"><a href="#44-KVC和KVO的keyPath一定是属性么?" class="headerlink" title="44.KVC和KVO的keyPath一定是属性么?"></a>44.KVC和KVO的keyPath一定是属性么?</h4><p>KVC支持实例变量,KVO只能手动支持实例变量的监听。</p>
<h4 id="45-如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?"><a href="#45-如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?" class="headerlink" title="45.如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?"></a>45.如何关闭默认的KVO的默认实现,并进入自定义的KVO实现?</h4><p>通过重写<code>+ (BOOL)automaticallyNotifiesObserversForKey:</code>方法对相应属性的返回为NO即可关闭自动触发的键值观察通知,为了实现手动观察通知,你需要在值改变前调用<code>willChangeValueForKey:</code>和值改变后调用<code>didChangeValueForKey:</code>(当你改变多个属性值时,可以嵌套这些调用)。如果是实现集合类型属性中的值发生改变的通知,则要调用<code>willChange:valueAtIndexes:ForKey:</code>和<code>didChange:valueAtIndexes:ForKey:</code>,不仅要指定key还要指定change的类型和改变的那些值的索引集合。</p>
<h4 id="46-apple用什么方式实现对一个对象的KVO?"><a href="#46-apple用什么方式实现对一个对象的KVO?" class="headerlink" title="46.apple用什么方式实现对一个对象的KVO?"></a>46.apple用什么方式实现对一个对象的KVO?</h4><p>当你观察一个对象时,运行时会创建一个新的类,这个类继承自该对象的类,并重写了被观察属性的setter方法,在重写的setter方法中会在调用原setter方法之前和之后分别调用<code>willChangeValueForKey:</code>和<code>didChangeValueForKey:</code>,以实现通知所有观察对象值的改变,最后通过isa交换(isa swizzling)把这个对象的isa指针指向这个新创建的子类,这样对象就变成了新创建的子类的实例。苹果还重写了class方法并返回原来的类,借此欺骗我们。</p>
<h4 id="47-IBOutlet连出来的视图属性为什么可以被设置成weak"><a href="#47-IBOutlet连出来的视图属性为什么可以被设置成weak" class="headerlink" title="47.IBOutlet连出来的视图属性为什么可以被设置成weak?"></a>47.IBOutlet连出来的视图属性为什么可以被设置成weak?</h4><p>因为IBOutlet连出来的视图是属于某个视图层级中的,最终会被控制器的view所包含,当xib/storyboard文件加载进内存中时,初始化的控制器便强引用了view,而后随着视图层级各自强引用了其子视图,包括IBOutlet连出来的视图,所以它的属性可以被设置为weak。</p>
<h4 id="48-IB中User-Defined-Runtime-Attributes如何使用?"><a href="#48-IB中User-Defined-Runtime-Attributes如何使用?" class="headerlink" title="48.IB中User Defined Runtime Attributes如何使用?"></a>48.IB中User Defined Runtime Attributes如何使用?</h4><p>它是通过KVC的方式配置一些IB未提供的属性,当IB被加载了时,会发送相应的消息<code>[xxx setValue:xxx forKeyPath:xxx]</code>,这个对象的KeyPath必须要存在,否则会报错。</p>
<h4 id="49-如何调试BAD-ACCESS错误"><a href="#49-如何调试BAD-ACCESS错误" class="headerlink" title="49.如何调试BAD_ACCESS错误"></a>49.如何调试BAD_ACCESS错误</h4><p>通过Xcode设置Diagnostics->Enable Zombie Objects,或者设置全局断点捕捉异常。</p>
<h4 id="50-lldb(gdb)常用的调试命令?"><a href="#50-lldb(gdb)常用的调试命令?" class="headerlink" title="50.lldb(gdb)常用的调试命令?"></a>50.lldb(gdb)常用的调试命令?</h4><p>po 打印对象;<br>n 跳至下一步;<br>更多调试命令可查看<a href="http://lldb.llvm.org/lldb-gdb.html" target="_blank" rel="external">the LLDB Debugger</a>。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8B%EF%BC%89.md#25-_objc_msgforward%E5%87%BD%E6%95%B0%E6%98%AF%E5%81%9A%E4%BB%80%E4%B9%88%E7%9A%84%E7%9B%B4%E6%8E%A5%E8%B0%83%E7%94%A8%E5%AE%83%E5%B0%86%E4%BC%9A%E5%8F%91%E7%94%9F%E4%BB%80%E4%B9%88" target="_blank" rel="external">《招聘一个靠谱的iOS》面试题参考答案(下)</a><br><a href="http://aaaboom.com/?p=34" target="_blank" rel="external">RunLoop系列之源码分析</a> by <a href="http://aaaboom.com/" target="_blank" rel="external">YEVEN</a><br><a href="https://opensource.apple.com/tarballs/CF/" target="_blank" rel="external">Core Foundation源码下载地址</a><br><a href="http://blog.sunnyxx.com/2014/10/15/behind-autorelease/" target="_blank" rel="external">黑幕背后的Autorelease</a></p>
]]></content>
<summary type="html">
工作这么些年,被面试和面试他人都有经历不少了,个人感觉一个优秀的面试者应该包含诚恳、谦虚、自信、上进这些特质!当然技术也是很重要的考察点,因此面试之前还是要充分准备的,除了要对项目加深熟悉外,刷一刷热门面试题也是有用的。
</summary>
<category term="iOS" scheme="http://5mengqi.cc/categories/ios/"/>
<category term="面试" scheme="http://5mengqi.cc/tags/interview/"/>
</entry>
<entry>
<title>iOS面试题收录(一)</title>
<link href="http://5mengqi.cc/blogs/include-ios-interview-questions-1/"/>
<id>http://5mengqi.cc/blogs/include-ios-interview-questions-1/</id>
<published>2017-03-23T09:57:55.000Z</published>
<updated>2017-03-31T03:03:47.000Z</updated>
<content type="html"><![CDATA[<h2 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h2><p>工作这么些年,被面试和面试他人都有经历不少了,个人感觉过程中除了技术考察外态度也很重要,诚恳、谦虚、自信、上进这些应该是一个优秀面试者该有的特质!回答问题要抓住重点,不知为不知即可,尽量别紧张,交流也是一种拓宽知识面、收获新知识的方式,往往面试官对你的提点会有所帮助。</p>
<h2 id="刷刷面试题"><a href="#刷刷面试题" class="headerlink" title="刷刷面试题"></a>刷刷面试题</h2><p>面试之前还是要做足准备的,除了对项目要加深熟悉外,刷一刷热门面试题也是有用的。以下为我的收录(皆可搜到,自刷方可熟记):</p>
<h4 id="1-什么情况使用weak关键字,相比assgin有什么不同"><a href="#1-什么情况使用weak关键字,相比assgin有什么不同" class="headerlink" title="1.什么情况使用weak关键字,相比assgin有什么不同"></a>1.什么情况使用weak关键字,相比assgin有什么不同</h4><p>weak使用场景有delegate代理属性、Xib/Storboard的IBOutlet视图属性,weak属性是指在setter设置方法里无保留新值、释放旧值操作,当指向的对象被销毁时,会自动置空(=nil),而assgin只是对纯量类型(scalar type,例如CGFloat、NSInteger)的简单赋值操作。</p>
<h4 id="2-这个写法会出什么问题:-property-copy-NSMutableArray-array"><a href="#2-这个写法会出什么问题:-property-copy-NSMutableArray-array" class="headerlink" title="2.这个写法会出什么问题:@property (copy) NSMutableArray *array;"></a>2.这个写法会出什么问题:@property (copy) NSMutableArray *array;</h4><p>可变数组copy后会变成不可变数组,再对其添加、删除、修改元素时,会因为找不到对应方法而崩溃;<br>使用atomic会在setter方法中加入锁机制严重影响性能。</p>
<h4 id="3-如何让自己的类用copy修饰符?如何重写带copy关键字的-setter?"><a href="#3-如何让自己的类用copy修饰符?如何重写带copy关键字的-setter?" class="headerlink" title="3.如何让自己的类用copy修饰符?如何重写带copy关键字的 setter?"></a>3.如何让自己的类用copy修饰符?如何重写带copy关键字的 setter?</h4><p>自定义类要支持copy方法,需要声明该类遵从NSCopying协议,并实现其协议方法<code>- (id)copyWithZone:(NSZone *)zone</code>,举例如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">MKUser</span><<span class="title">NSCopying</span>></span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *username;</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, assgin) <span class="built_in">NSUInteger</span> age;</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) <span class="built_in">NSMutableSet</span> *friends;</div><div class="line"></div><div class="line">- (<span class="keyword">instancetype</span>)initWithUsername:(<span class="built_in">NSString</span> *)username</div><div class="line"> age:(<span class="built_in">NSUInteger</span>)age {</div><div class="line"> <span class="keyword">if</span>(<span class="keyword">self</span> = [<span class="keyword">super</span> init]) {</div><div class="line"> _username = [username <span class="keyword">copy</span>];</div><div class="line"> _age = age;</div><div class="line"> _friends = [[<span class="built_in">NSMutableSet</span> alloc] init];</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">id</span>)copyWithZone:(<span class="built_in">NSZone</span> *)zone {</div><div class="line"> <span class="built_in">MKUser</span> *<span class="keyword">copy</span> = [[[<span class="keyword">self</span> <span class="keyword">class</span>] allocWithZone:zone]</div><div class="line"> initWithUsername:_username</div><div class="line"> age:_age];</div><div class="line"> <span class="keyword">copy</span>->_friends = [_friends mutableCopy];</div><div class="line"> <span class="keyword">return</span> <span class="keyword">copy</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 针对集合类型对象实现深拷贝即逐个复制集合中的元素</span></div><div class="line">- (<span class="keyword">id</span>)deepCopy {</div><div class="line"> <span class="built_in">MKUser</span> *<span class="keyword">copy</span> = [[[<span class="keyword">self</span> <span class="keyword">class</span>] alloc]</div><div class="line"> initWithUsername:_username</div><div class="line"> age:_age];</div><div class="line"> <span class="keyword">copy</span>->_friends = [[<span class="built_in">NSMutableSet</span> alloc] initWithSet:_friends copyItems:<span class="literal">YES</span>];</div><div class="line"> <span class="keyword">return</span> <span class="keyword">copy</span>;</div><div class="line">}</div><div class="line"></div><div class="line">...</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></p>
<p>重写copy语义的setter方法如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">- (<span class="keyword">void</span>)setUsername:(<span class="built_in">NSString</span> *)username {</div><div class="line"> <span class="keyword">if</span> (_username != username) {</div><div class="line"> _username = [username <span class="keyword">copy</span>];</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h4 id="4-property-的本质是什么?ivar、getter、setter-是如何生成并添加到这个类中的"><a href="#4-property-的本质是什么?ivar、getter、setter-是如何生成并添加到这个类中的" class="headerlink" title="4. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的"></a>4. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的</h4><p>@property本质是结构体(struct),runtime对@property的定义如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// runtime.h</span></div><div class="line"><span class="comment">/// An opaque type that represents an Objective-C declared property.</span></div><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> objc_property *objc_property_t;</div><div class="line"></div><div class="line"><span class="comment">/// Defines a property attribute</span></div><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> {</div><div class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *name; <span class="comment">/**< The name of the attribute */</span></div><div class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *value; <span class="comment">/**< The value of the attribute (usually empty) */</span></div><div class="line">} objc_property_attribute_t;</div><div class="line"></div><div class="line"><span class="comment">// objc-private.h</span></div><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> property_t *objc_property_t;</div><div class="line"></div><div class="line"><span class="comment">// objc-runtime-new.h</span></div><div class="line"><span class="keyword">struct</span> property_t {</div><div class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *name;<span class="comment">// 属性名</span></div><div class="line"> <span class="keyword">const</span> <span class="keyword">char</span> *attributes;<span class="comment">// 属性个数和objc_property_attribute_t中元素组成的字符串</span></div><div class="line">};</div></pre></td></tr></table></figure></p>
<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// TestClass.m</span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *string;</div><div class="line"></div><div class="line"><span class="keyword">unsigned</span> <span class="keyword">int</span> count = <span class="number">0</span>;</div><div class="line">objc_property_t *props = class_copyPropertyList([TestClass <span class="keyword">class</span>], &count);</div><div class="line"><span class="built_in">NSLog</span>(<span class="string">@"count %u, %s"</span>, count, property_getAttributes(*props));</div><div class="line"></div><div class="line"><span class="meta"># <span class="meta-string">"count 1, T@"</span>NSString<span class="meta-string">",C,N,V_string"</span></span></div></pre></td></tr></table></figure>
<p>实测结果中T表示类型、C表示copy、N表示nonatomic、V表示实例变量(_ivar)。</p>
<p><strong>ivar、getter、setter 是如何生成并添加到这个类中的?</strong><br>属性定义好后,编译器会在编译期自动合成(autosynthesis)存取方法(setter和getter),并且添加对应名称的实例变量(_ivar),其中可通过@synthesize指定不同的实例变量名称。<br>实现流程:每增加一个属性,系统都会在成员变量列表(ivar_list)中添加一个成员变量的描述,在方法列表(method_list)添加setter和getter方法的描述,在属性列表(prop_list)添加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出setter与getter方法对应的实现,在setter方法中从偏移量的位置开始赋值,在getter方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。</p>
<h4 id="5-protocol-和-category-中如何使用-property"><a href="#5-protocol-和-category-中如何使用-property" class="headerlink" title="5.@protocol 和 category 中如何使用 @property"></a>5.@protocol 和 category 中如何使用 @property</h4><p>协议(protocol)中使用@property是希望遵从此协议的对象实现setter和getter方法;<br>分类(category)中使用@property需要用到runtime的关联对象(associated object)。举个例子:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// NSObject+AssociatedObject.h</span></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">NSObject</span> (<span class="title">AssociatedObject</span>)</span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) <span class="keyword">id</span> associatedObject;</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="comment">// NSObject+AssociatedObject.m</span></div><div class="line"><span class="meta">#import <span class="meta-string"><objc/runtime.h></span></span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">NSObject</span> (<span class="title">AssociatedObject</span>)</span></div><div class="line"><span class="keyword">@dynamic</span> associatedObject;</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)setAssociatedObject:(<span class="keyword">id</span>)object {</div><div class="line"> objc_setAssociatedObject(<span class="keyword">self</span>, <span class="keyword">@selector</span>(associatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="keyword">id</span>)associatedObject {</div><div class="line"> <span class="keyword">return</span> objc_getAssociatedObject(<span class="keyword">self</span>, <span class="keyword">@selector</span>(associatedObject));</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></p>
<h4 id="6-runtime如何实现weak属性"><a href="#6-runtime如何实现weak属性" class="headerlink" title="6.runtime如何实现weak属性"></a>6.runtime如何实现weak属性</h4><p>系统将以weak指针指向对象的内存地址为key,所有weak指针为value的键值对存入一个全局weak引用表中,当这个对象被销毁时,会从weak表中搜出以key为键的所有weak指针,并遍历将weak指针指向nil对象。runtime代码摘录如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// NSObject.mm</span></div><div class="line"><span class="keyword">id</span> objc_initWeak(<span class="keyword">id</span> *location, <span class="keyword">id</span> newObj)</div><div class="line">{</div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line">template <<span class="keyword">bool</span> HaveOld, <span class="keyword">bool</span> HaveNew, <span class="keyword">bool</span> CrashIfDeallocating></div><div class="line"><span class="keyword">static</span> <span class="keyword">id</span> </div><div class="line">storeWeak(<span class="keyword">id</span> *location, objc_object *newObj)</div><div class="line">{</div><div class="line"> ...</div><div class="line"> newObj = (objc_object *)weak_register_no_lock(&newTable->weak_table, </div><div class="line"> (<span class="keyword">id</span>)newObj, location, </div><div class="line"> CrashIfDeallocating);</div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">struct</span> SideTable {</div><div class="line"> ...</div><div class="line"> weak_table_t weak_table;</div><div class="line"> ...</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">// objc-weak.h</span></div><div class="line"><span class="comment">/**</span></div><div class="line"> * The global weak references table. Stores object ids as keys,</div><div class="line"> * and weak_entry_t structs as their values.</div><div class="line"> */</div><div class="line"><span class="keyword">struct</span> weak_table_t {</div><div class="line"> weak_entry_t *weak_entries;</div><div class="line"> size_t num_entries;</div><div class="line"> uintptr_t mask;</div><div class="line"> uintptr_t max_hash_displacement;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">// objc-weak.mm</span></div><div class="line"><span class="comment">/** </span></div><div class="line"> * Registers a new (object, weak pointer) pair. Creates a new weak</div><div class="line"> * object entry if it does not exist.</div><div class="line"> * </div><div class="line"> * @param weak_table The global weak table.</div><div class="line"> * @param referent The object pointed to by the weak reference.</div><div class="line"> * @param referrer The weak pointer address.</div><div class="line"> */</div><div class="line"><span class="keyword">id</span> </div><div class="line">weak_register_no_lock(weak_table_t *weak_table, <span class="keyword">id</span> referent_id, </div><div class="line"> <span class="keyword">id</span> *referrer_id, <span class="keyword">bool</span> crashIfDeallocating)</div><div class="line">{</div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">/** </span></div><div class="line"> * Called by dealloc; nils out all weak pointers that point to the </div><div class="line"> * provided object so that they can no longer be used.</div><div class="line"> * </div><div class="line"> * @param weak_table </div><div class="line"> * @param referent The object being deallocated. </div><div class="line"> */</div><div class="line"><span class="keyword">void</span> </div><div class="line">weak_clear_no_lock(weak_table_t *weak_table, <span class="keyword">id</span> referent_id)</div><div class="line">{</div><div class="line"> ...</div><div class="line"> <span class="keyword">if</span> (*referrer == referent) {</div><div class="line"> *referrer = <span class="literal">nil</span>;</div><div class="line"> }</div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h4 id="7-property中有哪些属性关键字?-property-后面可以有哪些修饰符?"><a href="#7-property中有哪些属性关键字?-property-后面可以有哪些修饰符?" class="headerlink" title="7.@property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?"></a>7.@property中有哪些属性关键字?/ @property 后面可以有哪些修饰符?</h4><p>原子性:atomic(默认)、nonatomic<br>使用atomic,会在setter方法中加锁,其方案是自旋锁(spinlocks),而不是<code>@synchronized(self)</code>,其原因是自旋锁更快,对于属性设置这种相对快的操作更适合。在runtime中的实现:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// objc-accessors.mm</span></div><div class="line"><span class="keyword">void</span> objc_setProperty(<span class="keyword">id</span> <span class="keyword">self</span>, SEL _cmd, ptrdiff_t offset, <span class="keyword">id</span> newValue, <span class="built_in">BOOL</span> atomic, <span class="keyword">signed</span> <span class="keyword">char</span> shouldCopy) </div><div class="line">{</div><div class="line"> ...</div><div class="line"> reallySetProperty(<span class="keyword">self</span>, _cmd, newValue, offset, atomic, <span class="keyword">copy</span>, mutableCopy);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keyword">inline</span> <span class="keyword">void</span> reallySetProperty(<span class="keyword">id</span> <span class="keyword">self</span>, SEL _cmd, <span class="keyword">id</span> newValue, ptrdiff_t offset, <span class="keyword">bool</span> atomic, <span class="keyword">bool</span> <span class="keyword">copy</span>, <span class="keyword">bool</span> mutableCopy)</div><div class="line">{</div><div class="line"> ...</div><div class="line"> spinlock_t& slotlock = PropertyLocks[slot];</div><div class="line"> slotlock.lock();</div><div class="line"> oldValue = *slot;</div><div class="line"> *slot = newValue; </div><div class="line"> slotlock.unlock();</div><div class="line"> ...</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>读/写权限:readwrite(默认)、readonly<br>内存管理语义:assgin、strong、weak、unsafe_unretained、copy<br>unsafe_unretained属性对象被销毁时,其指针不会被指向nil对象,因此如果再调用这个销毁对象的方法,是会报错崩溃的。<br>方法名:<code>getter=<name></code>、<code>setter=<name></code><br><code>getter=<name></code>样式举例:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>, <span class="keyword">getter</span>=theNewOne) <span class="built_in">NSString</span> *newOne;</div></pre></td></tr></table></figure></p>
<p>不常用:nonnull、nullable、null_resettable<br>nonnull表示不可为nil<br>nullable表示可为nil<br>null_resettable表示setter nullable而getter nonnull</p>
<h4 id="8-synthesize和-dynamic分别有什么作用?"><a href="#8-synthesize和-dynamic分别有什么作用?" class="headerlink" title="8.@synthesize和@dynamic分别有什么作用?"></a>8.@synthesize和@dynamic分别有什么作用?</h4><p>@synthesize表示如果没有手动实现setter和getter,编译器会自动帮你合成,并添加相应类型的实例变量;<br>@dynamic表示setter和getter自行实现,声明@dynamic使编译器不会警告,但运行时如果setter和getter方法不存在并被调用的话会报错崩溃。</p>
<h4 id="9-用-property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?"><a href="#9-用-property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?" class="headerlink" title="9.用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?"></a>9.用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?</h4><p>使用copy会拷贝出一个新的不可变对象,这样保证了本对象不会有被改变的风险,如果使用strong声明,当这个指针指向的是一个可变对象时,这个对象假如在外部改变了,内部也会受影响。举例说明:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">strong</span>) <span class="built_in">NSString</span> *string;</div><div class="line"></div><div class="line"><span class="built_in">NSMutableString</span> *mutableString = [[<span class="built_in">NSMutableString</span> alloc] init];</div><div class="line"><span class="keyword">self</span>.string = mutableString;</div><div class="line">[mutableString appendString:<span class="string">@" hello monki!"</span>];</div><div class="line"><span class="built_in">NSLog</span>(<span class="string">@"\nmutableString(%p)=%@\n_string(%p)=%@"</span>, mutableString, mutableString, _string, _string);</div><div class="line"> </div><div class="line"><span class="keyword">self</span>.string = [mutableString <span class="keyword">copy</span>];</div><div class="line">[mutableString appendString:<span class="string">@" hello MengQi Yang!"</span>];</div><div class="line"><span class="built_in">NSLog</span>(<span class="string">@"\nmutableString(%p)=%@\n_string(%p)=%@"</span>, mutableString, mutableString, _string, _string);</div><div class="line"></div><div class="line"><span class="meta"># mutableString(0x608000262f00)= hello monki!</span></div><div class="line"><span class="meta"># _string(0x608000262f00)= hello monki!</span></div><div class="line"></div><div class="line"><span class="meta"># mutableString(0x608000262f00)= hello monki! hello MengQi Yang!</span></div><div class="line"><span class="meta"># _string(0x600000234780)= hello monki!</span></div></pre></td></tr></table></figure></p>
<p>延伸理解下深拷贝和浅拷贝:<br>对非集合类、不可变(immutable)对象操作,copy是指针拷贝,mutableCopy是内容拷贝,而对非集合类、可变(mutable)对象操作,都是内容拷贝。代码示例:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">[immutableObject <span class="keyword">copy</span>]<span class="comment">// 浅拷贝</span></div><div class="line">[immutableObject mutableCopy]<span class="comment">// 深拷贝</span></div><div class="line">[mutableObject <span class="keyword">copy</span>]<span class="comment">// 深拷贝</span></div><div class="line">[mutableObject mutableCopy]<span class="comment">// 深拷贝</span></div></pre></td></tr></table></figure></p>
<p>对集合类、不可变对象操作,copy是指针拷贝,mutableCopy是内容拷贝,而对集合类、可变对象操作,都是内容拷贝。注意:集合对象内容拷贝仅限于对象本身,对象元素仍然是指针拷贝。代码示例:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">[immutableSet <span class="keyword">copy</span>]<span class="comment">// 浅拷贝</span></div><div class="line">[immutableSet mutableCopy]<span class="comment">// 单层深拷贝</span></div><div class="line">[mutableSet <span class="keyword">copy</span>]<span class="comment">// 单层深拷贝</span></div><div class="line">[mutableSet mutableCopy]<span class="comment">// 单层深拷贝</span></div></pre></td></tr></table></figure></p>
<h4 id="10-synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为-foo的实例变量,那么还会自动合成新变量么?"><a href="#10-synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为-foo的实例变量,那么还会自动合成新变量么?" class="headerlink" title="10. @synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?"></a>10. @synthesize合成实例变量的规则是什么?假如property名为foo,存在一个名为_foo的实例变量,那么还会自动合成新变量么?</h4><p>规则如下:<br>1.如果指定了实例变量名称,会生成指定名称的实例变量<code>@synthesize foo = _foo</code><br>2.如果这个变量已经存在了就不再生成了<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">TestClass</span> : <span class="title">NSObject</span> </span>{</div><div class="line"> <span class="built_in">NSObject</span> *_foo;</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></p>
<p>3.如果没指定实例变量名称,会生成属性同名的实例变量<code>@synthesize foo</code></p>
<p><strong>假如 property 名为 foo,存在一个名为 _foo 的实例变量,那么还会自动合成新变量么?</strong><br>不会,示例如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">TestClass</span> () </span>{</div><div class="line"> <span class="built_in">NSString</span> *_string;</div><div class="line">}</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *string;</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *_string;</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="meta"># Warning:Auto property synthesis will not synthesize property '_string' because it cannot share an ivar with another synthesized property</span></div></pre></td></tr></table></figure></p>
<h4 id="11-在有了自动合成属性实例变量之后,-synthesize还有哪些使用场景?"><a href="#11-在有了自动合成属性实例变量之后,-synthesize还有哪些使用场景?" class="headerlink" title="11.在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?"></a>11.在有了自动合成属性实例变量之后,@synthesize还有哪些使用场景?</h4><p>场景包括:<br>1.重写setter和getter时<br>2.重写只读属性的getter时<br>3.protocol中定义的属性<br>4.父类中重载的属性</p>
<p>示例如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// BaseClass.h</span></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">BaseClass</span> : <span class="title">NSObject</span></span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *string4;</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="comment">// TestClass.h</span></div><div class="line"><span class="meta">#import <span class="meta-string">"BaseClass.h"</span></span></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">TestClass</span>: <span class="title">BaseClass</span></span></div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="comment">// TestClass.m</span></div><div class="line"><span class="class"><span class="keyword">@protocol</span> <span class="title">TestProtocol</span> <<span class="title">NSObject</span>></span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *string3;</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">TestClass</span> ()<<span class="title">TestProtocol</span>></span></div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *string1;</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>, <span class="keyword">readonly</span>) <span class="built_in">NSString</span> *string2;</div><div class="line"><span class="keyword">@property</span> (<span class="keyword">nonatomic</span>, <span class="keyword">copy</span>) <span class="built_in">NSString</span> *string4;</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">TestClass</span></span></div><div class="line"><span class="keyword">@synthesize</span> string1 = _string1;</div><div class="line"><span class="keyword">@synthesize</span> string2 = _string2;</div><div class="line"><span class="keyword">@synthesize</span> string3 = _string3;</div><div class="line"><span class="keyword">@synthesize</span> string4 = _string4;</div><div class="line"></div><div class="line">- (<span class="keyword">void</span>)setString1:(<span class="built_in">NSString</span> *)string1 {</div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="built_in">NSString</span> *)string1 {</div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line">- (<span class="built_in">NSString</span> *)string2 {</div><div class="line"> ...</div><div class="line">}</div><div class="line"></div><div class="line">...</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure></p>
<h4 id="12-objc中向一个nil对象发送消息将会发生什么?"><a href="#12-objc中向一个nil对象发送消息将会发生什么?" class="headerlink" title="12.objc中向一个nil对象发送消息将会发生什么?"></a>12.objc中向一个nil对象发送消息将会发生什么?</h4><p>在Objective-C中,发送消息直到运行时才会与方法执行绑定,编译器会将消息表达式转换为对<code>objc_msgSend</code>的调用,例如<code>[receiver message]</code>对应 <code>objc_msgSend(receiver, selector)</code>,如果消息带参数则为<code>objc_msgSend(receiver, selector, arg1, arg2, ...)</code>。发送消息的关键在于每个类或实例中都包含两个重要的元素即指向元类或所属类的指针和类的方法列表(存放着方法的名称与指针之间的映射),当消息被发送给对象时,会根据对象的isa指针找到其所属类,在该类的方法列表中去查找方法名称(selector),如果没找到就会去父类的方法列表中查找,直到NSObject,其间只要定位到selector,就会调用表中对应的方法执行。为了加快这个过程,系统会在这些方法被使用时缓存其名称和指针,而且每个类都有独立的缓存,它包含了继承的和自身的方法。在搜索方法列表前,程序会首先在接收对象的类的方法缓存中检查,如果在其中,那么发送消息也慢不了直接调用函数多少。<br>按上面的理解,如果receiver为nil的话,一开始便无法按isa找到所属类,并不会报什么错,<code>objc_msgSend</code>的返回值由消息返回类型决定。</p>
<h4 id="13-objc中向一个对象发送消息-obj-foo-和objc-msgSend-函数之间有什么关系?"><a href="#13-objc中向一个对象发送消息-obj-foo-和objc-msgSend-函数之间有什么关系?" class="headerlink" title="13.objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?"></a>13.objc中向一个对象发送消息[obj foo]和objc_msgSend()函数之间有什么关系?</h4><p>按12题的理解,发送消息[obj foo]会被转换为调用<code>objc_msgSend(obj, @selector(foo))</code>。使用命令<code>clang -rewrite-objc obj.m</code>将Objective-C编译为C++,以下为结果:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// obj.m</span></div><div class="line"><span class="meta">#import <span class="meta-string"><Foundation/Foundation.h></span></span></div><div class="line"><span class="class"><span class="keyword">@interface</span></span></div><div class="line"> Obj : <span class="built_in">NSObject</span> </div><div class="line"><span class="keyword">@end</span></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">Obj</span> </span></div><div class="line">- (<span class="keyword">void</span>)foo {</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[]) {</div><div class="line"> <span class="keyword">@autoreleasepool</span> {</div><div class="line"> Obj *obj = [[Obj alloc] init];</div><div class="line"> [obj foo];</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// obj.cpp</span></div><div class="line">...</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> * argv[])</span> </span>{</div><div class="line"> <span class="comment">/* @autoreleasepool */</span> { __AtAutoreleasePool __autoreleasepool; </div><div class="line"> Obj *obj = ((Obj *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)((Obj *(*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)objc_getClass(<span class="string">"Obj"</span>), sel_registerName(<span class="string">"alloc"</span>)), sel_registerName(<span class="string">"init"</span>));</div><div class="line"> ((<span class="keyword">void</span> (*)(id, SEL))(<span class="keyword">void</span> *)objc_msgSend)((id)obj, sel_registerName(<span class="string">"foo"</span>));</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure>
<h4 id="14-什么时候会报unrecognized-selector的异常?"><a href="#14-什么时候会报unrecognized-selector的异常?" class="headerlink" title="14.什么时候会报unrecognized selector的异常?"></a>14.什么时候会报unrecognized selector的异常?</h4><p>当调用某对象的某个方法,而该对象并没有实现这个方法时,会报”unrecognized selector”异常。接12题的理解,当在最顶层父类依然找不到对应的方法时,程序在运行时会崩溃并抛出异常,在此之前,runtime提供了三次拯救程序的机会:</p>
<p><strong>Dynamic Method Resolution</strong></p>
<p>首先runtime会调用<code>+resolveInstanceMethod:</code>或者<code>+resolveClassMethod:</code>,只要在其中提供一个方法实现,并返回YES,runtime会重启一次消息发送的过程。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">void anotherFoo(id obj, SEL _cmd) {</div><div class="line"> NSLog(@"Doing another foo");</div><div class="line">}</div><div class="line"></div><div class="line">+ (BOOL)resolveInstanceMethod:(SEL)selector {</div><div class="line"> if(selector == @selector(foo:)) {</div><div class="line"> class_addMethod([self class], selector, (IMP)anotherFoo, "v@:");</div><div class="line"> return YES;</div><div class="line"> }</div><div class="line"> return [super resolveInstanceMethod:selector];</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p><strong>Message Fast Forwarding</strong></p>
<p>如果<code>resolve</code>返回NO,runtime会调用<code>-forwardingTargetForSelector:</code>,通过它可将消息转发给另一个对象。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">- (<span class="keyword">id</span>)forwardingTargetForSelector:(SEL)selector {</div><div class="line"> <span class="keyword">if</span> (selector == <span class="keyword">@selector</span>(foo:)) {</div><div class="line"> <span class="keyword">return</span> alternateObject;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> [<span class="keyword">super</span> forwardingTargetForSelector:selector];</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>只要返回的不是nil和self,整个消息发送过程就会重启,当然发送对象会变成你返回的那个对象,否则会继续Normal Fowarding。之所以叫Fast,是为了区分下一步的转发,因为这一步不会创建任何新的对象,而下一步会创建一个NSInvocation对象,所以相对更快点。</p>
<p><strong>Message Normal Forwarding</strong></p>
<p>如果Fast Forwarding没有新的对象返回,runtime会先发送<code>methodSignatureForSelector:</code>消息。如果其返回nil,runtime会发出<code>-doesNotRecognizeSelector:</code>消息,程序崩溃。如果返回一个方法签名,runtime会创建一个NSInvocation对象并发送<code>-forwardInvocation:</code>消息。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">- (<span class="built_in">NSMethodSignature</span> *)methodSignatureForSelector:(SEL)selector {</div><div class="line"> <span class="built_in">NSMethodSignature</span> *signature = [<span class="keyword">super</span> methodSignatureForSelector:selector];</div><div class="line"> <span class="keyword">if</span> (!signature) {</div><div class="line"> signature = [alternateObject methodSignatureForSelector:selector];</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> signature;</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>NSInvocation对象是用来存储和转发消息的,可配置selector、target以及参数(self、_cmd分别占用索引0、1)等信息。所以可在<code>-forwardInvocation:</code>中修改NSInvocation对象,然后发送<code>-invokeWithTarget:</code>消息。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">- (<span class="keyword">void</span>)forwardInvocation:(<span class="built_in">NSInvocation</span> *)anInvocation {</div><div class="line"> <span class="keyword">if</span> ([alternatedObject respondsToSelector:anInvocation.selector]) {</div><div class="line"> [anInvocation invokeWithTarget:alternatedObject];</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">return</span> [<span class="keyword">super</span> forwardInvocation:anInvocation];</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h4 id="15-一个objc对象如何进行内存布局?(考虑有父类的情况)"><a href="#15-一个objc对象如何进行内存布局?(考虑有父类的情况)" class="headerlink" title="15.一个objc对象如何进行内存布局?(考虑有父类的情况)"></a>15.一个objc对象如何进行内存布局?(考虑有父类的情况)</h4><p>在Objective-C中,每一个objc对象都是一个类的实例,每个对象都有一个名为isa的指针,指向该对象的所属类,这个类记录了实例变量列表、实例方法列表等等,其中的superclass指针所要描述的是继承关系,继承链中最终指向的根父类是NSObject,而NSObject的superclass指向nil。<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// NSObject.h</span></div><div class="line">...</div><div class="line"><span class="meta">#include <span class="meta-string"><objc/objc.h></span></span></div><div class="line">...</div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">NSObject</span> <<span class="title">NSObject</span>> </span>{</div><div class="line"> Class isa OBJC_ISA_AVAILABILITY;</div><div class="line">}</div><div class="line"></div><div class="line">...</div><div class="line"><span class="comment">// NSObject.mm</span></div><div class="line">...</div><div class="line"></div><div class="line">- (<span class="keyword">id</span>)init {</div><div class="line"> <span class="keyword">return</span> _objc_rootInit(<span class="keyword">self</span>);</div><div class="line">}</div><div class="line"></div><div class="line">...</div><div class="line"></div><div class="line"><span class="keyword">id</span></div><div class="line">_objc_rootInit(<span class="keyword">id</span> obj)</div><div class="line">{</div><div class="line"> <span class="comment">// In practice, it will be hard to rely on this function.</span></div><div class="line"> <span class="comment">// Many classes do not properly chain -init calls.</span></div><div class="line"> <span class="keyword">return</span> obj;</div><div class="line">}</div><div class="line"></div><div class="line">...</div><div class="line"><span class="comment">// objc.h</span></div><div class="line">...</div><div class="line"><span class="comment">/// An opaque type that represents an Objective-C class.</span></div><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> objc_class *Class;</div><div class="line"></div><div class="line"><span class="comment">/// Represents an instance of a class.</span></div><div class="line"><span class="keyword">struct</span> objc_object {</div><div class="line"> Class isa OBJC_ISA_AVAILABILITY;</div><div class="line">};</div><div class="line"></div><div class="line"><span class="comment">/// A pointer to an instance of a class.</span></div><div class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> objc_object *<span class="keyword">id</span>;</div><div class="line">...</div><div class="line"><span class="comment">// objc-runtime-old.h</span></div><div class="line">...</div><div class="line"></div><div class="line"><span class="keyword">struct</span> objc_class : objc_object {</div><div class="line"> Class superclass;</div><div class="line"> ...</div><div class="line"> <span class="keyword">struct</span> old_ivar_list *ivars;</div><div class="line"> <span class="keyword">struct</span> old_method_list **methodLists;</div><div class="line"> ...</div><div class="line"></div><div class="line">}</div><div class="line"></div><div class="line">...</div><div class="line"><span class="comment">// objc-private.h</span></div><div class="line">...</div><div class="line"></div><div class="line"><span class="keyword">struct</span> objc_object {</div><div class="line">private:</div><div class="line"> isa_t isa;</div><div class="line"></div><div class="line">public:</div><div class="line"></div><div class="line"> <span class="comment">// ISA() assumes this is NOT a tagged pointer object</span></div><div class="line"> Class ISA();</div><div class="line"></div><div class="line"> <span class="comment">// getIsa() allows this to be a tagged pointer object</span></div><div class="line"> Class getIsa();</div><div class="line"></div><div class="line">...</div><div class="line">}</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>由上面摘录的runtime代码,也可以看出类其实也是一个对象,其必然也含有一个isa指针,指向的是元类(metaclass),元类保存有类方法列表,当一个类方法被调用时,会在其元类的类方法列表中查找selector,如果没有,则该元类会通过superclass去其父类查找,直到NSObject。元类也是一个对象,那么元类的isa指针又指向哪里呢?为了设计上的完整,所有元类的isa指针都会指向根元类(root metaclass即NSObject的元类),而根元类的isa指针指向自己,这样就形成一个闭环。下面这张图比较直观的描述了isa和继承的关系:<br><img src="/images/class-diagram.jpg" alt=""></p>
<h4 id="16-一个objc对象的isa的指针指向什么?有什么作用?"><a href="#16-一个objc对象的isa的指针指向什么?有什么作用?" class="headerlink" title="16.一个objc对象的isa的指针指向什么?有什么作用?"></a>16.一个objc对象的isa的指针指向什么?有什么作用?</h4><p>按15题的理解,指向它的类对象,从方法列表中查找selector。</p>
<h4 id="17-下面的代码输出什么?"><a href="#17-下面的代码输出什么?" class="headerlink" title="17.下面的代码输出什么?"></a>17.下面的代码输出什么?</h4><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">Son</span> : <span class="title">Father</span></span></div><div class="line">- (<span class="keyword">id</span>)init</div><div class="line"> {</div><div class="line"> <span class="keyword">self</span> = [<span class="keyword">super</span> init];</div><div class="line"> <span class="keyword">if</span> (<span class="keyword">self</span>) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, <span class="built_in">NSStringFromClass</span>([<span class="keyword">self</span> <span class="keyword">class</span>]));</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, <span class="built_in">NSStringFromClass</span>([<span class="keyword">super</span> <span class="keyword">class</span>]));</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line"> }</div><div class="line"><span class="keyword">@end</span></div></pre></td></tr></table></figure>
<p>都输出Son,此题考察的是对self和super的理解,我们都知道self是指向当前调用方法的这个类的实例的指针,由此很多人会想当然的认为super应该是指向父类的实例的指针,而其实不然,在Objective-C中super实质是一个编译器标示符,和self一样指向同一个消息接收者,不同点在于super会告诉编译器,调用方法时,要去父类中查找,而不是本类。通过命令<code>clang -rewrite-objc test.m</code>编译为C++代码关键处如下:<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 修改题目代码</span></div><div class="line"><span class="comment">// test.m</span></div><div class="line"><span class="meta">#import <span class="meta-string"><Foundation/Foundation.h></span></span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">Father</span> : <span class="title">NSObject</span> </span></div><div class="line"><span class="keyword">@end</span></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">Father</span></span></div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">@interface</span> <span class="title">Son</span> : <span class="title">Father</span></span></div><div class="line"><span class="keyword">@end</span></div><div class="line"><span class="class"><span class="keyword">@implementation</span> <span class="title">Son</span></span></div><div class="line">- (<span class="keyword">id</span>)init {</div><div class="line"> <span class="keyword">self</span> = [<span class="keyword">super</span> init];</div><div class="line"> <span class="keyword">if</span> (<span class="keyword">self</span>) {</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, <span class="built_in">NSStringFromClass</span>([<span class="keyword">self</span> <span class="keyword">class</span>]));</div><div class="line"> <span class="built_in">NSLog</span>(<span class="string">@"%@"</span>, <span class="built_in">NSStringFromClass</span>([<span class="keyword">super</span> <span class="keyword">class</span>]));</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line">}</div><div class="line"><span class="keyword">@end</span></div><div class="line"></div><div class="line"><span class="comment">// test.cpp</span></div><div class="line">...</div><div class="line"></div><div class="line"><span class="comment">// @implementation Son</span></div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="keyword">id</span> _I_Son_init(Son * <span class="keyword">self</span>, SEL _cmd) {</div><div class="line"> <span class="keyword">self</span> = ((Son *(*)(__rw_objc_super *, SEL))(<span class="keyword">void</span> *)objc_msgSendSuper)((__rw_objc_super){(<span class="keyword">id</span>)<span class="keyword">self</span>, (<span class="keyword">id</span>)class_getSuperclass(objc_getClass(<span class="string">"Son"</span>))}, sel_registerName(<span class="string">"init"</span>));</div><div class="line"> <span class="keyword">if</span> (<span class="keyword">self</span>) {</div><div class="line"> <span class="built_in">NSLog</span>((<span class="built_in">NSString</span> *)&__NSConstantStringImpl__var_folders_5p_yqf_zqt55f9_qf32zwnjnrkc0000gn_T_test_08005e_mi_0, <span class="built_in">NSStringFromClass</span>(((Class (*)(<span class="keyword">id</span>, SEL))(<span class="keyword">void</span> *)objc_msgSend)((<span class="keyword">id</span>)<span class="keyword">self</span>, sel_registerName(<span class="string">"class"</span>))));</div><div class="line"> <span class="built_in">NSLog</span>((<span class="built_in">NSString</span> *)&__NSConstantStringImpl__var_folders_5p_yqf_zqt55f9_qf32zwnjnrkc0000gn_T_test_08005e_mi_1, <span class="built_in">NSStringFromClass</span>(((Class (*)(__rw_objc_super *, SEL))(<span class="keyword">void</span> *)objc_msgSendSuper)((__rw_objc_super){(<span class="keyword">id</span>)<span class="keyword">self</span>, (<span class="keyword">id</span>)class_getSuperclass(objc_getClass(<span class="string">"Son"</span>))}, sel_registerName(<span class="string">"class"</span>))));</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">self</span>;</div><div class="line"> }</div><div class="line"><span class="comment">// @end</span></div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>从上面代码,我们发现[self class]被转换为objc_msgSend,runtime中定义为<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// message.h</span></div><div class="line">...</div><div class="line">OBJC_EXPORT <span class="keyword">id</span> objc_msgSend(<span class="keyword">id</span> <span class="keyword">self</span>, SEL op, ...)</div><div class="line"> OBJC_AVAILABLE(<span class="number">10.0</span>, <span class="number">2.0</span>, <span class="number">9.0</span>, <span class="number">1.0</span>);</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>self作为第一参数传进去,[super class]被转换为objc_msgSendSuper,runtime中定义为<br><figure class="highlight objectivec"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// message.h</span></div><div class="line">...</div><div class="line"></div><div class="line"><span class="comment">/// Specifies the superclass of an instance. </span></div><div class="line"><span class="keyword">struct</span> objc_super {</div><div class="line"> <span class="comment">/// Specifies an instance of a class.</span></div><div class="line"> __<span class="keyword">unsafe_unretained</span> <span class="keyword">id</span> receiver;</div><div class="line"></div><div class="line"> <span class="comment">/// Specifies the particular superclass of the instance to message. </span></div><div class="line"><span class="meta">#if !defined(__cplusplus) && !__OBJC2__</span></div><div class="line"> <span class="comment">/* For compatibility with old objc-runtime.h header */</span></div><div class="line"> __<span class="keyword">unsafe_unretained</span> Class <span class="keyword">class</span>;</div><div class="line"><span class="meta">#else</span></div><div class="line"> __<span class="keyword">unsafe_unretained</span> Class super_class;</div><div class="line"><span class="meta">#endif</span></div><div class="line"> <span class="comment">/* super_class is the first class to search */</span></div><div class="line">};</div><div class="line">...</div><div class="line"></div><div class="line">OBJC_EXPORT <span class="keyword">id</span> objc_msgSendSuper(<span class="keyword">struct</span> objc_super *<span class="keyword">super</span>, SEL op, ...)</div><div class="line"> OBJC_AVAILABLE(<span class="number">10.0</span>, <span class="number">2.0</span>, <span class="number">9.0</span>, <span class="number">1.0</span>);</div><div class="line"></div><div class="line">...</div><div class="line"><span class="comment">// NSObject.mm</span></div><div class="line">...</div><div class="line"></div><div class="line">- (Class)<span class="keyword">class</span> {</div><div class="line"> <span class="keyword">return</span> object_getClass(<span class="keyword">self</span>);</div><div class="line">}</div><div class="line"></div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>objc_super作为第一参数传入objc_msgSendSuper,self作为结构体objc_super的第一个成员,第二个记录的是当前类的父类。<br>所以,当调用[self class]时,实际是调用obj_msgSend,第一个参数是Son类型的实例,然后在Son类对象的方法列表中去找class方法,没有则去父类Father中找,也没有,最终会在NSObject类中找到,而class的实现就是返回self的类,故输出Son。而当调用[super class]时,实际是调用objc_msgSendSuper,会先构造objc_super结构体,self是其第一个成员,第二个是<code>(id)class_getSuperclass(objc_getClass("Son"))</code>返回Father类,接着从Father类对象的方法列表中查找class方法,没有,最终也会在NSObject类中找到,class返回的是objc_super->receiver即self的类,故也输出Son。</p>
<h4 id="18-runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)"><a href="#18-runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)" class="headerlink" title="18.runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)"></a>18.runtime如何通过selector找到对应的IMP地址?(分别考虑类方法和实例方法)</h4><p>结合15题理解,实例方法是从对象所属类的方法列表中查找selector并获得对应的IMP指针的,类方法则是从所属类的元类的方法列表中查找selector并获得对应的IMP指针的。</p>
<h4 id="19-使用runtime-Associate方法关联的对象,需要在主对象dealloc的时候释放么?"><a href="#19-使用runtime-Associate方法关联的对象,需要在主对象dealloc的时候释放么?" class="headerlink" title="19.使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?"></a>19.使用runtime Associate方法关联的对象,需要在主对象dealloc的时候释放么?</h4><p>不需要,根据<a href="https://developer.apple.com/videos/wwdc/2011/#322-video" target="_blank" rel="external">WWDC 2011, Session 322, 36:22</a>中发布的内存销毁时间表:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">1.调用release,引用计数为0</div><div class="line"> * 对象正在被销毁,生命周期即将结束</div><div class="line"> * 新的__weak弱引用是不允许的,并且旧的将指向nil</div><div class="line"> * 调用[self dealloc]</div><div class="line">2.子类调用dealloc</div><div class="line"> * 最底层子类调用dealloc</div><div class="line"> * 非ARC代码手动释放实例变量</div><div class="line"> * 继承链上的父类调用dealloc</div><div class="line">3.NSObject调用dealloc</div><div class="line"> * 仅仅调用Objc runtime的object_dispose()函数</div><div class="line">4.object_dispose()</div><div class="line"> * 调用C++实例变量的销毁函数</div><div class="line"> * 调用ARC实例变量的release</div><div class="line"> * 清除关联对象的引用</div><div class="line"> * 清除__weak对象的引用</div><div class="line"> * 调用free()</div></pre></td></tr></table></figure></p>
<p>可以看到关联对象会在被NSObject的dealloc方法调用的object_dispose()函数中释放。</p>
<h4 id="20-objc中的类方法和实例方法有什么本质区别和联系?"><a href="#20-objc中的类方法和实例方法有什么本质区别和联系?" class="headerlink" title="20.objc中的类方法和实例方法有什么本质区别和联系?"></a>20.objc中的类方法和实例方法有什么本质区别和联系?</h4><p>类方法:<br>1.类方法属于元类对象<br>2.类方法只能被类对象调用<br>3.类方法中的self是类对象<br>4.类方法可以调用其他的类方法<br>5.类方法中不能访问实例变量<br>6.类方法中不能直接调用实例方法<br>实例方法:<br>1.实例方法属于类对象<br>2.实例方法只能被实例对象调用<br>3.实例方法中的self是实例对象<br>4.实例方法中可以访问实例变量<br>5.实例方法中可以调用类方法</p>
<p><strong>此处可跳转第二篇<a href="http://5mengqi.cc/blogs/include-ios-interview-questions-2/">iOS面试题收录(二)</a></strong></p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://github.com/ChenYilong/iOSInterviewQuestions/blob/master/01%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88/%E3%80%8A%E6%8B%9B%E8%81%98%E4%B8%80%E4%B8%AA%E9%9D%A0%E8%B0%B1%E7%9A%84iOS%E3%80%8B%E9%9D%A2%E8%AF%95%E9%A2%98%E5%8F%82%E8%80%83%E7%AD%94%E6%A1%88%EF%BC%88%E4%B8%8A%EF%BC%89.md" target="_blank" rel="external">《招聘一个靠谱的iOS》面试题参考答案(上)</a><br><a href="https://github.com/monkiyang/objc-runtime" target="_blank" rel="external">objc-runtime源码</a><br><a href="http://blog.devtang.com/2013/10/15/objective-c-object-model/" target="_blank" rel="external">Objective-C对象模型及应用</a> by <a href="http://blog.devtang.com/" target="_blank" rel="external">唐巧</a><br><a href="http://ios.jobbole.com/81657/" target="_blank" rel="external">Objective-C中的元类(meta class)是什么?</a><br><a href="http://tech.glowing.com/cn/objective-c-runtime/" target="_blank" rel="external">Objective-C Runtime</a> by <a href="http://tech.glowing.com/cn/" target="_blank" rel="external">Glow 技术团队博客</a></p>
]]></content>
<summary type="html">
工作这么些年,被面试和面试他人都有经历不少了,个人感觉一个优秀的面试者应该包含诚恳、谦虚、自信、上进这些特质!当然技术也是很重要的考察点,因此面试之前还是要充分准备的,除了要对项目加深熟悉外,刷一刷热门面试题也是有用的。
</summary>
<category term="iOS" scheme="http://5mengqi.cc/categories/ios/"/>
<category term="面试" scheme="http://5mengqi.cc/tags/interview/"/>
</entry>
<entry>
<title>搭建个人博客</title>
<link href="http://5mengqi.cc/blogs/build-personal-blog/"/>
<id>http://5mengqi.cc/blogs/build-personal-blog/</id>
<published>2016-05-27T16:00:00.000Z</published>
<updated>2017-03-16T16:06:52.000Z</updated>
<content type="html"><![CDATA[<h2 id="写在开头"><a href="#写在开头" class="headerlink" title="写在开头"></a>写在开头</h2><p>一直有想法弄个博客来写东西,可怯于自己的水平、文笔怕写不出什么内容来,加之人也有点惰性blabla,也是拖了好久,现在想着其实没必要在意这么多,网络的世界重在分享!每个人成长所处阶段并不一样,总结出来的也不都正确,要敢于暴露自己的错误,才更能收获知识的正确。技术类博文对很多技术人是有导向性的,如果知识传递不正确,很多人的理解就会偏差,所以还是要尽量深入的去研究再总结,博文也应该随着技术更新而更新,交流也是很重要的。</p>
<h2 id="Github-Pages"><a href="#Github-Pages" class="headerlink" title="Github Pages"></a>Github Pages</h2><p>先来聊聊如何搭建博客吧!Github Pages提供了免费域名(类似<code>http://username.github.io</code>)来搭建网页,有以下几个步骤:</p>
<p>1.申请一个GitHub账号-><a href="https://github.com/join" target="_blank" rel="external">地址</a><br>2.创建一个名为<code>username.github.io</code>的仓库(<code>username</code>填写GitHub用户名,请确保一致,否则会报错404)<br>3.使用你偏爱的方式克隆此仓库,此处以Terminal方式来介绍(包括GUI工具<a href="https://www.sourcetreeapp.com/" target="_blank" rel="external">SourceTree</a>或<a href="https://desktop.github.com/" target="_blank" rel="external">GitHub</a>)<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># 克隆仓库到本地</span></div><div class="line">$ git <span class="built_in">clone</span> https://github.com/username/username.github.io</div><div class="line"></div><div class="line"><span class="comment"># Hello World首页</span></div><div class="line">$ <span class="built_in">cd</span> username.github.io</div><div class="line">$ <span class="built_in">echo</span> <span class="string">"Hello World"</span> > index.html</div><div class="line"></div><div class="line"><span class="comment"># 推送远端</span></div><div class="line">$ git add <span class="_">-a</span></div><div class="line">$ git commit -m <span class="string">"Initial commit"</span></div><div class="line">$ git push</div></pre></td></tr></table></figure></p>
<p>4.浏览器输入<code>http://username.github.io</code>访问Hello World页面</p>
<h2 id="Hexo"><a href="#Hexo" class="headerlink" title="Hexo"></a>Hexo</h2><p>Hexo提供了很多精美的<a href="http://hexo.io/themes/" target="_blank" rel="external">博客主题模板</a>,还有很多便利的<a href="http://hexo.io/plugins/" target="_blank" rel="external">插件</a>,要使用此博客引擎首先要安装Node.js-><a href="https://nodejs.org/en/" target="_blank" rel="external">官网地址</a>,可用<code>$ node -v</code>或<code>$ npm -v</code>命令检测版本,接着通过<code>$ npm install hexo-cli -g</code>命令安装Hexo即可。</p>
<h4 id="使用介绍"><a href="#使用介绍" class="headerlink" title="使用介绍"></a>使用介绍</h4><p><strong>初始化博客</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># 博客目录下</span></div><div class="line">$ hexo init</div><div class="line">$ npm install</div></pre></td></tr></table></figure></p>
<p><strong>修改主题</strong><br>可在/themes目录下查看博客主题,初始默认提供landscape主题,要更换的话,需要先从远端仓库克隆到/themes目录下,例如:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git <span class="built_in">clone</span> https://github.com/monkiyang/hexo-theme-polarbear.git themes/polarbear</div></pre></td></tr></table></figure></p>
<p>然后修改_config.yml文件中themes配置即<code>themes:polarbear</code></p>
<p><strong>预览博客</strong><br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ hexo s</div><div class="line"></div><div class="line"><span class="comment"># INFO Start processing</span></div><div class="line"><span class="comment"># INFO Hexo is running at http://localhost:4000/. Press Ctrl+C to stop.</span></div></pre></td></tr></table></figure></p>
<p>浏览器输入<code>http://localhost:4000/</code>预览本地博客。</p>
<p><strong>写文章</strong><br><code>$ hexo new title</code>会在.source/_posts目录下生成一个.md文件,用Markdown编辑好内容,<code>hexo s</code>预览效果。</p>
<p><strong>部署博客</strong><br><code>hexo g</code>会生成一个public目录,里面包括你所有的网页及资源。注意:每次部署前,先执行<code>hexo g</code>,因为部署会提取public目录下的内容推送至远端仓库。修改_config.yml文件中deploy配置为<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">deploy:</div><div class="line"> type: git</div><div class="line"> repo: [email protected]:username/username.github.io.git</div><div class="line">branch: master</div></pre></td></tr></table></figure></p>
<p>然后<code>$ npm install hexo-deployer-git -save</code>命令安装hexo-deployer-git插件,最后执行<code>$ hexo -d</code>,等上传成功后,就能通过<code>http://username.github.io</code>访问博客。</p>
<h2 id="个性域名"><a href="#个性域名" class="headerlink" title="个性域名"></a>个性域名</h2><p>1.在<a href="https://wanwang.aliyun.com/" target="_blank" rel="external">阿里云万网</a>付费注册一个域名<br>2.在username.github.io仓库创建一个CNAME文件,在其中加入购买的域名<br>3.在阿里云万网配置域名解析指向<code>http://username.github.io</code><br>4.浏览器输入个性域名访问博客</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p><a href="https://help.github.com/categories/github-pages-basics/" target="_blank" rel="external">Github Pages帮助文档</a><br><a href="https://hexo.io/zh-cn/docs/" target="_blank" rel="external">Hexo帮助文档</a><br><a href="http://dapanggit.github.io/jekyll-to-hexo/" target="_blank" rel="external">成功叛逃到hexo</a> by <a href="http://dapanggit.github.io/" target="_blank" rel="external">Lyeec</a></p>
]]></content>
<summary type="html">
一直有想法弄个博客来写东西,可怯于自己的水平、文笔怕写不出什么内容来,加之人也有点惰性blabla,也是拖了好久,现在想着其实没必要在意这么多,网络的世界重在分享!so先来聊聊如何搭建博客吧!
</summary>
<category term="其它" scheme="http://5mengqi.cc/categories/other/"/>
<category term="Github Pages" scheme="http://5mengqi.cc/tags/github-pages/"/>
<category term="Hexo" scheme="http://5mengqi.cc/tags/hexo/"/>
</entry>
</feed>