Skip to content

Commit b660473

Browse files
committed
为每道题添加讨论区链接
1 parent 843b7be commit b660473

File tree

2 files changed

+129
-39
lines changed

2 files changed

+129
-39
lines changed

01《招聘一个靠谱的iOS》面试题参考答案/《招聘一个靠谱的iOS》面试题参考答案(上).md

Lines changed: 57 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ typedef NS_ENUM(NSInteger, CYLGender) {
251251
```
252252
属性的参数应该按照下面的顺序排列: 原子性,读写 和 内存管理。 这样做你的属性更容易修改正确,并且更好阅读。这在[《禅与Objective-C编程艺术 >》](https://github.com/oa414/objc-zen-book-cn#属性定义)里有介绍。而且习惯上修改某个属性的修饰符时,一般从属性名从右向左搜索需要修动的修饰符。最可能从最右边开始修改这些属性的修饰符,根据经验这些修饰符被修改的可能性从高到底应为:内存管理 > 读写权限 >原子操作。
253253

254+
讨论区: [《个人认为,UserModel还是比起User的命名方式好些 #21]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/21)
255+
254256
#### ***硬伤部分***
255257

256258
1. 在-和(void)之间应该有一个空格
@@ -297,7 +299,7 @@ age 设计为 NSUInteger类型,外部只读,
297299

298300
Objective-C 中诸如 NSArray 中的 count 返回的是 NSUInteger 是一个非常不优雅的设计, Swift 中的 Array 的 count 就选择使用 Int 。强制要用 `NSUInteger` 的地方就是 `bitmask` , Objective-C 中叫 NS_OPTION ,因为要消除不同的编译器的 `right shift` 到底是 `arithmetic right shift` 还是 `logical right shift` 的歧义。
299301

300-
302+
如果对硬伤部分有疑问,欢迎参与讨论: [《硬伤部分 #49](https://github.com/ChenYilong/iOSInterviewQuestions/issues/49)
301303

302304
### 2. 什么情况使用 weak 关键字,相比 assign 有什么不同?
303305
什么情况使用 weak 关键字?
@@ -309,13 +311,13 @@ Objective-C 中诸如 NSArray 中的 count 返回的是 NSUInteger 是一个非
309311

310312
不同点:
311313

312-
1. `weak` 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似,
313-
然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。
314-
`assign` 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或
315-
NSlnteger 等)的简单赋值操作。
314+
1. `weak` 修饰符表明该属性定义了一种“非拥有关系” (nonowning relationship)。在为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此行为与 assign 类似,不同之处在于,在 weak 属性所指的对象遭到销毁、释放时,该属性值也会清空(nil out)。而 `assign` 的“设置方法”只会执行针对“纯量类型/基本数据类型” (scalar type,例如 CGFloat 或
315+
NSInteger 等)的简单赋值操作。
316316

317317
2. assign 可以用非 OC 对象,而 weak 必须用于 OC 对象
318318

319+
其他讨论见: [《第2题 #89]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/89 )
320+
319321
### 3. 怎么用 copy 关键字?
320322
用途:
321323

@@ -421,6 +423,8 @@ self.mutableArray = array;
421423
因此,开发iOS程序时一般都会使用 nonatomic 属性。但是在开发 Mac OS X 程序时,使用
422424
atomic 属性通常都不会有性能瓶颈。
423425

426+
如果对题有疑问,可参考讨论区: [《第四题 #62](https://github.com/ChenYilong/iOSInterviewQuestions/issues/62)
427+
424428
### 5. 如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
425429

426430

@@ -479,6 +483,7 @@ self.mutableArray = array;
479483
return copy;
480484
}
481485
```
486+
482487
但在实际的项目中,不可能这么简单,遇到更复杂一点,比如类对象中的数据结构可能并未在初始化方法中设置好,需要另行设置。举个例子,假如 CYLUser 中含有一个数组,与其他 CYLUser 对象建立或解除朋友关系的那些方法都需要操作这个数组。那么在这种情况下,你得把这个包含朋友对象的数组也一并拷贝过来。下面列出了实现此功能所需的全部代码:
483488

484489
```Objective-C
@@ -556,16 +561,6 @@ typedef NS_ENUM(NSInteger, CYLGender) {
556561
return copy;
557562
}
558563
559-
- (id)deepCopy {
560-
CYLUser *copy = [[[self class] alloc]
561-
initWithName:_name
562-
age:_age
563-
gender:_gender];
564-
copy->_friends = [[NSMutableSet alloc] initWithSet:_friends
565-
copyItems:YES];
566-
return copy;
567-
}
568-
569564
@end
570565
571566
```
@@ -592,6 +587,8 @@ typedef NS_ENUM(NSInteger, CYLGender) {
592587

593588
```
594589

590+
注意:由于上文中 `CYLUser``-copyWithZone` 方法里,`_friends` 成员的的赋值使用的 `- mutableCopy` 是浅拷贝,只是创建了`NSMutableSet` 对象; 导致 `- deepCopy` 方法中, `_friends` 的每一个对象的 `_friends` 列表并未创建实例。如需继续优化,还需要改造。参见这里的讨论:https://github.com/ChenYilong/iOSInterviewQuestions/pull/24 欢迎大家可以在链接 issue 中贡献自己的想法进行讨论
591+
595592
至于***如何重写带 copy 关键字的 setter***这个问题,
596593

597594

@@ -620,7 +617,7 @@ typedef NS_ENUM(NSInteger, CYLGender) {
620617

621618

622619

623-
这样真得高效吗?不见得!这种写法“看上去很美、很合理”,但在实际开发中,它更像下图里的做法:
620+
这样真得高效吗?不见得!这种写法“看上去很美、很合理”,但在 ARC 时代的实际开发中,它更像下图里的做法:
624621

625622
![https://github.com/ChenYilong](http://i.imgur.com/UwV9oSn.jpeg)
626623

@@ -634,8 +631,6 @@ typedef NS_ENUM(NSInteger, CYLGender) {
634631
> 老百姓 copy 一下,咋就这么难?
635632
636633

637-
638-
639634
你可能会说:
640635

641636

@@ -662,11 +657,12 @@ typedef NS_ENUM(NSInteger, CYLGender) {
662657
[a setX:[a x]]; //队友咆哮道:你在干嘛?!!
663658
```
664659

665-
> 不要在 setter 里进行像 `if (_obj != newObj)` 这样的判断。(该观点参考链接:[ ***How To Write Cocoa Object Setters: Principle 3: Only Optimize After You Measure*** ](http://vgable.com/blog/tag/autorelease/)
660+
> ARC时代下,不要在 setter 里进行像 `if (_obj != newObj)` 这样的判断。(该观点参考链接:[ ***How To Write Cocoa Object Setters: Principle 3: Only Optimize After You Measure*** ](http://vgable.com/blog/tag/autorelease/)
666661
667662

668663

669-
什么情况会在 copy setter 里做 if 判断?
664+
ARC时代下,什么情况会在 copy setter 里做 if 判断?
665+
670666
例如,车速可能就有最高速的限制,车速也不可能出现负值,如果车子的最高速为300,则 setter 的方法就要改写成这样:
671667

672668

@@ -700,7 +696,11 @@ typedef NS_ENUM(NSInteger, CYLGender) {
700696
```
701697
702698
703-
699+
讨论区:
700+
701+
- [《set中,对if (_name != name)的描述 #10》]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/10 )
702+
- [《更新问题 “如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?” 的答案 #24》]( https://github.com/ChenYilong/iOSInterviewQuestions/pull/24 )
703+
704704
### 6. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的
705705
706706
**@property 的本质是什么?**
@@ -744,6 +744,8 @@ typedef NS_ENUM(NSInteger, CYLGender) {
744744
- (void)setLastName:(NSString *)lastName;
745745
@end
746746
```
747+
748+
对上面这一句有疑问,可参考讨论区: [《第6题 上述代码写出来的类与下面这种写法等效: #86》]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/86 )
747749
748750
**更新**:
749751
@@ -802,12 +804,29 @@ typedef struct {
802804

803805
1. `OBJC_IVAR_$类名$属性名称` :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
804806
2. setter 与 getter 方法对应的实现函数
805-
2. `ivar_list` :成员变量列表
806-
2. `method_list` :方法列表
807-
2. `prop_list` :属性列表
807+
3. `ivar_list` :成员变量列表
808+
4. `method_list` :方法列表
809+
5. `prop_list` :属性列表
810+
811+
812+
也就是说我们每次在增加一个属性,系统都会在 `ivar_list` 中添加一个成员变量的描述,在 `method_list` 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转。
813+
814+
注意:其中 prop_list 存在哪里?
815+
816+
```c
817+
//objc-runtime-new.h中
818+
struct objc_class : objc_object {
819+
//...
820+
class_data_bits_t bits;//在bits.data()里面
821+
//...
822+
}
823+
```
824+
825+
讨论见: [《第六题 prop_list 存在哪里? #108]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/108 )
808826

827+
828+
809829

810-
也就是说我们每次在增加一个属性,系统都会在 `ivar_list` 中添加一个成员变量的描述,在 `method_list` 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.
811830

812831
### 7. @protocol 和 category 中如何使用 @property
813832

@@ -817,6 +836,8 @@ typedef struct {
817836
1. `objc_setAssociatedObject`
818837
2. `objc_getAssociatedObject`
819838

839+
对该回答有疑问,可参考讨论区 [《第7题,在代理里定义属性,好像没有使用场景吧 #83]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/83 )
840+
820841
### 8. runtime 如何实现 weak 属性
821842

822843
要实现 weak 属性,首先要搞清楚 weak 属性的特点:
@@ -889,7 +910,8 @@ struct weak_table_t {
889910

890911
`objc_storeWeak`函数把第二个参数--赋值对象(b)的内存地址作为键值key,将第一个参数--weak修饰的属性变量(a)的内存地址(&a)作为value,注册到 weak 表中。如果第二个参数(b)为0(nil),那么把变量(a)的内存地址(&a)从weak表中删除,
891912

892-
你可以把`objc_storeWeak(&a, b)`理解为:`objc_storeWeak(value, key)`,并且当key变nil,将value置nil。
913+
你可以把`objc_storeWeak(&a, b)`理解为:`objc_storeWeak(value, key)`,并且当key变nil,将value置nil。(如对这句话有疑问,可以参考讨论 [《第8题 感觉objc_storeWeak(&a, b) 理解有点问题 #98](https://github.com/ChenYilong/iOSInterviewQuestions/issues/98) )
914+
893915

894916
在b非nil时,a和b指向同一个内存地址,在b变nil时,a变nil。此时向a发送消息不会崩溃:在Objective-C中向nil发送消息是安全的。
895917

@@ -1254,6 +1276,8 @@ void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL ato
12541276
2. @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
12551277
3. @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 `instance.var = someVar`,由于缺 setter 方法会导致程序崩溃;或者当运行到 `someVar = var` 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
12561278

1279+
讨论区: [《上篇第11题,@dynamic那里说明有点问题 #26]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/26 )
1280+
12571281
### 12. ARC下,不显式指定任何属性关键字时,默认的关键字都有哪些?
12581282

12591283
1 对应基本数据类型默认关键字是
@@ -1330,6 +1354,11 @@ void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL ato
13301354
1. 对非集合类对象的 copy 与 mutableCopy 操作;
13311355
2. 对集合类对象的 copy 与 mutableCopy 操作。
13321356

1357+
讨论区:
1358+
1359+
- [《13题好像只有NSString符合你说的前两点特性 #29]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/29 )
1360+
- [《第13题 疑问 对非集合类对象的copy操作 #19]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/19 )
1361+
13331362
#### 1. 对非集合类对象的copy操作:
13341363

13351364
在非集合类对象中:对 immutable 对象进行 copy 操作,是指针复制,mutableCopy 操作时内容复制;对 mutable 对象进行 copy 和 mutableCopy 都是内容复制。用代码简单表示如下:
@@ -1553,7 +1582,7 @@ Person * motherInlaw = [[aPerson spouse] mother];
15531582
```
15541583

15551584

1556-
如果 spouse 对象为 nil,那么发送给 nil 的消息 mother 也将返回 nil。
1585+
如果 spouse 方法的返回值为 nil,那么发送给 nil 的消息 mother 也将返回 nil。
15571586

15581587
2、 如果方法返回值为指针类型,其指针大小为小于或者等于sizeof(void*),float,double,long double 或者 long long 的整型标量,发送给 nil 的消息将返回0。
15591588

@@ -1861,6 +1890,7 @@ objc在向一个对象发送消息时,runtime库会根据对象的isa指针找
18611890

18621891
我在仓库里也给出了一个相应的 Demo(名字叫:Demo_21题_下面的代码输出什么)。有兴趣可以跑起来看一下,主要看下他是怎么打印的,思考下为什么这么打印。
18631892

1893+
如果对这个例子有疑问:可以参与讨论区讨论 [《21题“不推荐在 init 方法中使用点语法” #75]( https://github.com/ChenYilong/iOSInterviewQuestions/issues/75 )
18641894

18651895
接下来让我们利用 runtime 的相关知识来验证一下 super 关键字的本质,使用clang重写命令:
18661896

0 commit comments

Comments
 (0)