diff --git "a/01\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210/\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210\357\274\210\344\270\212\357\274\211.md" "b/01\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210/\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210\357\274\210\344\270\212\357\274\211.md" index d306f0b..cd7e6a6 100644 --- "a/01\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210/\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210\357\274\210\344\270\212\357\274\211.md" +++ "b/01\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210/\343\200\212\346\213\233\350\201\230\344\270\200\344\270\252\351\235\240\350\260\261\347\232\204iOS\343\200\213\351\235\242\350\257\225\351\242\230\345\217\202\350\200\203\347\255\224\346\241\210\357\274\210\344\270\212\357\274\211.md" @@ -409,224 +409,21 @@ self.mutableArray = array; return copy; } ``` -但在实际的项目中,不可能这么简单,遇到更复杂一点,比如类对象中的数据结构可能并未在初始化方法中设置好,需要另行设置。举个例子,假如 CYLUser 中含有一个数组,与其他 CYLUser 对象建立或解除朋友关系的那些方法都需要操作这个数组。那么在这种情况下,你得把这个包含朋友对象的数组也一并拷贝过来。下面列出了实现此功能所需的全部代码: - -```Objective-C -// .h文件 -// http://weibo.com/luohanchenyilong/ -// https://github.com/ChenYilong -// 以第一题《风格纠错题》里的代码为例 - -typedef NS_ENUM(NSInteger, CYLSex) { - CYLSexMan, - CYLSexWoman -}; - -@interface CYLUser : NSObject - -@property (nonatomic, readonly, copy) NSString *name; -@property (nonatomic, readonly, assign) NSUInteger age; -@property (nonatomic, readonly, assign) CYLSex sex; - -- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex; -+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex; -- (void)addFriend:(CYLUser *)user; -- (void)removeFriend:(CYLUser *)user; - -@end -``` - -// .m文件 - - ```Objective-C -// .m文件 -// http://weibo.com/luohanchenyilong/ -// https://github.com/ChenYilong -// - -@implementation CYLUser { - NSMutableSet *_friends; -} - -- (void)setName:(NSString *)name { - _name = [name copy]; -} - -- (instancetype)initWithName:(NSString *)name - age:(NSUInteger)age - sex:(CYLSex)sex { - if(self = [super init]) { - _name = [name copy]; - _age = age; - _sex = sex; - _friends = [[NSMutableSet alloc] init]; - } - return self; -} - -- (void)addFriend:(CYLUser *)user { - [_friends addObject:user]; -} - -- (void)removeFriend:(CYLUser *)user { - [_friends removeObject:person]; -} - -- (id)copyWithZone:(NSZone *)zone { - CYLUser *copy = [[[self class] allocWithZone:zone] - initWithName:_name - age:_age - sex:_sex]; - copy->_friends = [_friends mutableCopy]; - return copy; -} - -- (id)deepCopy { - CYLUser *copy = [[[self class] allocWithZone:zone] - initWithName:_name - age:_age - sex:_sex]; - copy->_friends = [[NSMutableSet alloc] initWithSet:_friends - copyItems:YES]; - return copy; -} - -@end - - ``` - -以上做法能满足基本的需求,但是也有缺陷: - -> 如果你所写的对象需要深拷贝,那么可考虑新增一个专门执行深拷贝的方法。 - -【注:深浅拷贝的概念,在下文中有介绍,详见下文的:***用@property声明的 NSString(或NSArray,NSDictionary)经常使用 copy 关键字,为什么?如果改用 strong 关键字,可能造成什么问题?***】 - -在例子中,存放朋友对象的 set 是用 “copyWithZone:” 方法来拷贝的,这种浅拷贝方式不会逐个复制 set 中的元素。若需要深拷贝的话,则可像下面这样,编写一个专供深拷贝所用的方法: - - - ```Objective-C -- (id)deepCopy { - CYLUser *copy = [[[self class] allocWithZone:zone] - initWithName:_name - age:_age - sex:_sex]; - copy->_friends = [[NSMutableSet alloc] initWithSet:_friends - copyItems:YES]; - return copy; -} - - ``` - -至于***如何重写带 copy 关键字的 setter***这个问题, - - -如果抛开本例来回答的话,如下: - - - -```Objective-C -- (void)setName:(NSString *)name { - //[_name release]; - _name = [name copy]; -} -``` - -不过也有争议,有人说“苹果如果像下面这样干,是不是效率会高一些?” - +***重写带 copy 关键字的 setter***: ```Objective-C - (void)setName:(NSString *)name { if (_name != name) { - //[_name release];//MRC +#if __has_feature(objc_arc) + [_name release]; +#endif _name = [name copy]; } } ``` - - -这样真得高效吗?不见得!这种写法“看上去很美、很合理”,但在实际开发中,它更像下图里的做法: - -![enter image description here](http://i.imgur.com/UwV9oSn.jpeg) - -克强总理这样评价你的代码风格: - -![enter image description here](http://i.imgur.com/N77Lkic.png) - -我和总理的意见基本一致: - - -> 老百姓 copy 一下,咋就这么难? - - - - -你可能会说: - - -之所以在这里做`if判断` 这个操作:是因为一个 if 可能避免一个耗时的copy,还是很划算的。 -(在刚刚讲的:《如何让自己的类用 copy 修饰符?》里的那种复杂的copy,我们可以称之为 “耗时的copy”,但是对 NSString 的 copy 还称不上。) - - -但是你有没有考虑过代价: - - -> 你每次调用 `setX:` 都会做 if 判断,这会让 `setX:` 变慢,如果你在 `setX:`写了一串复杂的 `if+elseif+elseif+...` 判断,将会更慢。 - -要回答“哪个效率会高一些?”这个问题,不能脱离实际开发,就算 copy 操作十分耗时,if 判断也不见得一定会更快,除非你把一个“ @property他当前的值 ”赋给了他自己,代码看起来就像: - -```Objective-C -[a setX:x1]; -[a setX:x1]; //你确定你要这么干?与其在setter中判断,为什么不把代码写好? -``` - -或者 - - -```Objective-C -[a setX:[a x]]; //队友咆哮道:你在干嘛?!! -``` - -> 不要在 setter 里进行像 `if(_obj != newObj)` 这样的判断。(该观点参考链接:[ ***How To Write Cocoa Object Setters: Principle 3: Only Optimize After You Measure*** ](http://vgable.com/blog/tag/autorelease/) -) - - -什么情况会在 copy setter 里做 if 判断? -例如,车速可能就有最高速的限制,车速也不可能出现负值,如果车子的最高速为300,则 setter 的方法就要改写成这样: - - -```Objective-C --(void)setSpeed:(int)_speed{ - if(_speed < 0) speed = 0; - if(_speed > 300) speed = 300; - _speed = speed; -} -``` - - - -回到这个题目,如果单单就上文的代码而言,我们不需要也不能重写 name 的 setter :由于是 name 是只读属性,所以编译器不会为其创建对应的“设置方法”,用初始化方法设置好属性值之后,就不能再改变了。( 在本例中,之所以还要声明属性的“内存管理语义”--copy,是因为:如果不写 copy,该类的调用者就不知道初始化方法里会拷贝这些属性,他们有可能会在调用初始化方法之前自行拷贝属性值。这种操作多余而低效)。 - -那如何确保 name 被 copy?在初始化方法(initializer)中做: - - ```Objective-C - - (instancetype)initWithName:(NSString *)name - age:(NSUInteger)age - sex:(CYLSex)sex { - if(self = [super init]) { - _name = [name copy]; - _age = age; - _sex = sex; - _friends = [[NSMutableSet alloc] init]; - } - return self; - } - - ``` - - ###6. @property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的