iOS 经典全部面试题(上)

//错误,不要使用"and"来阐明有多个参数

  • (instancetype)initWithName:(CGFloat)width andAge:(CGFloat)height;

//正确,使用"and"来表示两个相对独立的操作

  • (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
  1. 由于字符串值可能会改变,所以要把相关属性的“内存管理语义”声明为 copy 。(原因在下文有详细论述:***用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?***)

  2. “性别”(sex)属性的:该类中只给出了一种“初始化方法” (initializer)用于设置“姓名”(Name)和“年龄”(Age)的初始值,那如何对“性别”(Sex)初始化?

Objective-C 有 designated 和 secondary 初始化方法的观念。 designated 初始化方法是提供所有的参数,secondary 初始化方法是一个或多个,并且提供一个或者更多的默认参数来调用 designated 初始化方法的初始化方法。举例说明:

// .m文件

// http://weibo.com/luohanchenyilong/

// https://github.com/ChenYilong

//

@implementation CYLUser

  • (instancetype)initWithName:(NSString *)name

age:(NSUInteger)age

sex:(CYLSex)sex {

if(self = [super init]) {

_name = [name copy];

_age = age;

_sex = sex;

}

return self;

}

  • (instancetype)initWithName:(NSString *)name

age:(NSUInteger)age {

return [self initWithName:name age:age sex:nil];

}

@end

上面的代码中initWithName:age:sex: 就是 designated 初始化方法,另外的是 secondary 初始化方法。因为仅仅是调用类实现的 designated 初始化方法。

因为出题者没有给出 .m 文件,所以有两种猜测:1:本来打算只设计一个 designated 初始化方法,但漏掉了“性别”(sex)属性。那么最终的修改代码就是上文给出的第一种修改方法。2:不打算初始时初始化“性别”(sex)属性,打算后期再修改,如果是这种情况,那么应该把“性别”(sex)属性设为 readwrite 属性,最终给出的修改代码应该是:

// .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, readwrite, assign) CYLSex sex;

  • (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;

  • (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;

  • (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;

@end

.h 中暴露 designated 初始化方法,是为了方便子类化 (想了解更多,请戳–》 ***《禅与 Objective-C 编程艺术 (Zen and the Art of the Objective-C Craftsmanship 中文翻译)》***。)

  • 按照接口设计的惯例,如果设计了“初始化方法” (initializer),也应当搭配一个快捷构造方法。而快捷构造方法的返回值,建议为 instancetype,为保持一致性,init 方法和快捷构造方法的返回类型最好都用 instancetype。

  • 如果基于第一种修改方法:既然该类中已经有一个“初始化方法” (initializer),用于设置“姓名”(Name)、“年龄”(Age)和“性别”(Sex)的初始值: 那么在设计对应 @property 时就应该尽量使用不可变的对象:其三个属性都应该设为“只读”。用初始化方法设置好属性值之后,就不能再改变了。在本例中,仍需声明属性的“内存管理语义”。于是可以把属性的定义改成这样

@property (nonatomic, readonly, copy) NSString *name;

@property (nonatomic, readonly, assign) NSUInteger age;

@property (nonatomic, readonly, assign) CYLSex sex;

由于是只读属性,所以编译器不会为其创建对应的“设置方法”,即便如此,我们还是要写上这些属性的语义,以此表明初始化方法在设置这些属性值时所用的方式。要是不写明语义的话,该类的调用者就不知道初始化方法里会拷贝这些属性,他们有可能会在调用初始化方法之前自行拷贝属性值。这种操作多余而且低效。

  1. initUserModelWithUserName 如果改为 initWithName 会更加简洁,而且足够清晰。

  2. UserModel 如果改为 User 会更加简洁,而且足够清晰。

  3. UserSex如果改为Sex 会更加简洁,而且足够清晰。

  4. 第二个 @property 中 assign 和 nonatomic 调换位置。 推荐按照下面的格式来定义属性

@property (nonatomic, readwrite, copy) NSString *name;

属性的参数应该按照下面的顺序排列: 原子性,读写 和 内存管理。 这样做你的属性更容易修改正确,并且更好阅读。这在《禅与Objective-C编程艺术 >》里有介绍。而且习惯上修改某个属性的修饰符时,一般从属性名从右向左搜索需要修动的修饰符。最可能从最右边开始修改这些属性的修饰符,根据经验这些修饰符被修改的可能性从高到底应为:内存管理 > 读写权限 >原子操作。

硬伤部分
  1. 在-和(void)之间应该有一个空格

  2. enum 中驼峰命名法和下划线命名法混用错误:枚举类型的命名规则和函数的命名规则相同:命名时使用驼峰命名法,勿使用下划线命名法。

  3. enum 左括号前加一个空格,或者将左括号换到下一行

  4. enum 右括号后加一个空格

  5. UserModel :NSObject 应为UserModel : NSObject,也就是:右侧少了一个空格。

  6. @interface 与 @property 属性声明中间应当间隔一行。

  7. 两个方法定义之间不需要换行,有时为了区分方法的功能也可间隔一行,但示例代码中间隔了两行。

  8. -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age; 方法中方法名与参数之间多了空格。而且 - 与 (id) 之间少了空格。

  9. -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age; 方法中方法名与参数之间多了空格:(NSString*)name 前多了空格。

  10. -(id)initUserModelWithUserName: (NSString*)name withAge:(int)age; 方法中 (NSString*)name,应为 (NSString *)name,少了空格。

  11. ~doLogIn方法中的 `LogIn` 命名不清晰:笔者猜测是login的意思,应该是粗心手误造成的。~

(勘误: Login 是名词, LogIn 是动词,都表示登陆的意思。见: Log in vs. login )

2. 什么情况使用 weak 关键字,相比 assign 有什么不同?

什么情况使用 weak 关键字?

  1. 在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性

  2. 自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak;当然,也可以使用strong。在下文也有论述:***《IBOutlet连出来的视图属性为什么可以被设置成weak?》***

不同点:

  1. weak 此特质表明该属性定义了一种“非拥有关系” (nonowning relationship)。为这种属性设置新值时,设置方法既不保留新值,也不释放旧值。此特质同assign类似, 然而在属性所指的对象遭到摧毁时,属性值也会清空(nil out)。 而 assign 的“设置方法”只会执行针对“纯量类型” (scalar type,例如 CGFloat 或 NSlnteger 等)的简单赋值操作。

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

3. 怎么用 copy 关键字?

用途:

  1. NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;

  2. block 也经常使用 copy 关键字,具体原因见官方文档:Objects Use Properties to Keep Track of Blocks

block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。你也许会感觉我这种做法有些怪异,不需要写依然写。如果你这样想,其实是你“日用而不知”,你平时开发中是经常在用我说的这种做法的,比如下面的属性不写copy也行,但是你会选择写还是不写呢?

@property (nonatomic, copy) NSString *userId;

  • (instancetype)initWithUserId:(NSString *)userId {

self = [super init];

if (!self) {

return nil;

}

_userId = [userId copy];

return self;

}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面做下解释: copy 此特质所表达的所属关系与 strong 类似。然而设置方法并不保留新值,而是将其“拷贝” (copy)。 当属性类型为 NSString 时,经常用此特质来保护其封装性,因为传递给设置方法的新值有可能指向一个 NSMutableString 类的实例。这个类是 NSString 的子类,表示一种可修改其值的字符串,此时若是不拷贝字符串,那么设置完属性之后,字符串的值就可能会在对象不知情的情况下遭人更改。所以,这时就要拷贝一份“不可变” (immutable)的字符串,确保对象中的字符串值不会无意间变动。只要实现属性所用的对象是“可变的” (mutable),就应该在设置新属性值时拷贝一份。

用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作,为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。

该问题在下文中也有论述:用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

4. 这个写法会出什么问题: @property (copy) NSMutableArray *array;

两个问题:1、添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃.因为 copy 就是复制一个不可变 NSArray 的对象;2、使用了 atomic 属性会严重影响性能 ;

第1条的相关原因在下文中有论述***《用@property声明的NSString(或NSArray,NSDictionary)经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?》*** 以及上文***《怎么用 copy 关键字?》***也有论述。

比如下面的代码就会发生崩溃

// .h文件

// http://weibo.com/luohanchenyilong/

// https://github.com/ChenYilong

// 下面的代码就会发生崩溃

@property (nonatomic, copy) NSMutableArray *mutableArray;

// .m文件

// http://weibo.com/luohanchenyilo

  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值