正文
####1、#import跟 #include 有什么区别?@class有什么作用?#import<> 跟 #import“ ”有什么区别?
答:
1)、 #import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入。
2)、@class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含。
3)、#import<>用来包含系统的头文件,#import“ ”用来包含用户头文件,现使用cocoapods导入的第三方库文件,一般也使用#import<>来导入。
####2、frame 和 bounds 有什么不同?
frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父view的坐标系统)
bounds指的是:该view在本身坐标系统中的位置和大小。(参照点是本身坐标系统)
####3、Objective-C的类可以多重继承么?可以实现多个接口么?Category是什么?重写一个类的方式用继承好还是分类好?为什么?
答:Objective-C的类不可以多重继承;可以实现多个接口(协议);Category是类别,一般用于给原有类添加新的方法;一般情况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其他类与原有类的关系。
####4、@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的?
@property = ivar + getter + setter;
“属性” (property)有两大概念:ivar(实例变量)、getter+setter(存取方法)
“属性” (property)作为 Objective-C 的一项特性,主要的作用就在于封装对象中的数据。 Objective-C 对象通常会把其所需要的数据保存为各种实例变量。实例变量一般通过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。
####5、@property中有哪些属性关键字? @property 后面可以有哪些修饰符?
属性可以拥有的特质分为四类:
1)、原子性--- nonatomic 特质
2)、读/写权限---readwrite(读写)、readonly (只读)
3)、内存管理语义---assign、strong、 weak、unsafe_unretained、copy
4)、方法名---getter= 、setter=
5)、不常用的:nonnull,null_resettable,nullable
####6、属性关键字 readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?
答:
1)、 readwrite 是可读可写特性。需要生成getter方法和setter方法。
2)、readonly 是只读特性。只会生成getter方法,不会生成setter方法,不希望属性在类外改变。
3)、 assign 是赋值特性。setter方法将传入参数赋值给实例变量;仅设置变量时,assign用于基本数据类型。
4)、retain(MRC)/strong(ARC) 表示持有特性。setter方法将传入参数先保留,再赋值,传入参数的retaincount会+1。
5)、 copy 表示拷贝特性。setter方法将传入对象复制一份,需要完全一份新的变量时。
6)、nonatomic 非原子操作。决定编译器生成的setter和getter方法是否是原子操作,atomic表示多线程安全,一般使用nonatomic,效率高。
####7、什么情况使用 weak 关键字,相比 assign 有什么不同?
1)、在 ARC 中,在有可能出现循环引用的时候,往往要通过让其中一端使用 weak 来解决,比如: delegate 代理属性。
2)、自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性一般也使用 weak,因为父控件的subViews数组已经对它有一个强引用;当然,也可以使用strong。
不同点:
assign 可以用于非 OC 对象,而 weak 必须用于 OC 对象。
weak 表明该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。
####8、怎么用 copy 关键字?
用途:
NSString、NSArray、NSDictionary 等等经常使用copy关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
block 也经常使用 copy 关键字。
说明:
block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 可以把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 还是 strong 效果是一样的,但写上 copy 也无伤大雅,还能时刻提醒我们:编译器自动对 block 进行了 copy 操作。如果不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操作”,他们有可能会在调用之前自行拷贝属性值。这种操作多余而低效。
####9、用@property声明的 NSString / NSArray / NSDictionary 经常使用 copy 关键字,为什么?如果改用strong关键字,可能造成什么问题?
答:用 @property 声明 NSString、NSArray、NSDictionary 经常使用 copy 关键字,是因为他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操作(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无意间变动,应该在设置新属性值时拷贝一份。还有就是因为父类指针可以指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 无论给我传入是一个可变对象还是不可对象,我本身持有的就是一个不可变的副本。
如果我们使用是 strong ,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改了,那么会影响该属性。
总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无意间篡改不可变类型对象原来的值。
####10、浅拷贝和深拷贝的区别?
答:
浅拷贝:只复制指向对象的指针,而不复制引用对象本身。
深拷贝:复制引用对象本身。内存中存在了两份独立对象本身,当修改A时,A_copy不变。
####11、系统对象的 copy 与 mutableCopy 方法
不管是集合类对象(NSArray、NSDictionary、NSSet ... 之类的对象),还是非集合类对象(NSString, NSNumber ... 之类的对象),接收到copy和mutableCopy消息时,都遵循以下准则:
1)、copy 返回的是不可变对象(immutableObject);如果用copy返回值调用mutable对象的方法就会crash。
2)、mutableCopy 返回的是可变对象(mutableObject)。
1)、非集合类对象的copy与mutableCopy
在非集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
对可变对象进行copy和mutableCopy都是内容复制。用代码简单表示如下:
2)、集合类对象的copy与mutableCopy (同上)
在集合类对象中,对不可变对象进行copy操作,是指针复制,mutableCopy操作是内容复制;
对可变对象进行copy和mutableCopy都是内容复制。但是:集合对象的内容复制仅限于对象本身,对集合内的对象元素仍然是指针复制。(即单层内容复制)
【总结一句话】:
只有对不可变对象进行copy操作是指针复制(浅复制),其它情况都是内容复制(深复制)!
####12、这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr;
问题:添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃。
如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460
copy后返回的是不可变对象(即 arr 是 NSArray 类型,NSArray 类型对象不能调用 NSMutableArray 类型对象的方法)
原因:是因为 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。
####13、如何让自己的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
若想令自己所写的对象具有拷贝功能,则需实现 NSCopying 协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。
具体步骤:
1)、 需声明该类遵从 NSCopying 协议
2)、实现 NSCopying 协议的方法。
// 该协议只有一个方法:
- (id)copyWithZone:(NSZone *)zone;
// 注意:使用 copy 修饰符,调用的是copy方法,其实真正需要实现的是 “copyWithZone” 方法。
@interface TestCopy () <NSCopying,NSMutableCopying>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *lastName;
@property (nonatomic, assign) NSInteger age;
@end
@implementation TestCopy {
NSString *_privatePro;
}
- (id)copyWithZone:(nullable NSZone *)zone {
TestCopy *model = [[[self class] allocWithZone:zone] init];
model.name = self.name;
model.lastName = self.lastName;
//未公开的成员变量
model->_privatePro = _privatePro;
return model;
}
-(id)mutableCopyWithZone:(NSZone *)zone {
TestCopy *model = [[[self class] allocWithZone:zone] init];
model.name = self.name;
model.lastName = self.lastName;
//未公开的成员变量
model->_privatePro = _privatePro;
return model;
}
@end
####14、写一个 setter 方法用于完成 @property (nonatomic, retain) NSString
name,写一个 setter 方法用于完成 @property (nonatomic, copy) NSString
name
答:
####15、
@synthesize 和 @dynamic 分别有什么作用?
@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。
如果@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var;
在类的实现代码里通过 @synthesize 语法可以来指定实例变量的名字。(@synthesize var = _newVar;)
1)、 @synthesize 的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
2)、 @dynamic 告诉编译器,属性的setter与getter方法由用户自己实现,不自动生成(如,@dynamic var)。
####16、
常见的 Objective-C 的数据类型有那些,和C的基本数据类型有什么区别?如:NSInteger和int
答:
Objective-C的数据类型有NSString,NSNumber,NSArray,NSMutableArray,NSData等等,这些都是class,创建后便是对象,而C语言的基本数据类型int,只是一定字节的内存空间,用于存放数值;NSInteger是基本数据类型,并不是NSNumber的子类,当然也不是NSObject的子类。NSInteger是基本数据类型Int或者Long的别名(NSInteger的定义typedef long NSInteger),它的区别在于,NSInteger会根据系统是32位还是64位来决定是本身是int还是long。
####17、
id 声明的对象有什么特性?
答:id 声明的对象具有运行时的特性,即可以指向任意类型的Objcetive-C的对象。
####18、
Objective-C 如何对内存管理的,说说你的看法和解决方法?
答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1)、自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。
2)、手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。
3)、 内存释放池Release Pool:把需要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中所有的内存空间也被自动释放掉。内存池的释放操作分为自动和手动。自动释放受runloop机制影响。
####19、
Objective-C 中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
答:线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用
####20、
Category(类别)、 Extension(扩展)和继承的区别
区别:
1)、 分类有名字,类扩展没有分类名字,是一种特殊的分类。
2)、 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展可以扩展属性、成员变量和方法。
3)、 继承可以增加,修改或者删除方法,并且可以增加属性。
####21、
我们说的OC是动态运行时语言是什么意思?
答:主要是将数据类型的确定由编译时,推迟到了运行时。简单来说, 运行时机制使我们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
####22、
为什么我们常见的delegate属性都用是week而不是retain/strong?
答:是为了防止delegate两端产生不必要的循环引用。
####23、
什么时候用delete,什么时候用Notification?
Delegate(委托模式):1对1的反向消息通知功能。
Notification(通知模式):只想要把消息发送出去,告知某些状态的变化。但是并不关心谁想要知道这个。
####24、
什么是 KVO 和 KVC?
1)、 KVC(Key-Value-Coding):键值编码 是一种通过字符串间接访问对象的方式(即给属性赋值)
举例说明:
stu.name = @"张三" // 点语法给属性赋值
[stu set Value:@"张三" for Key:@"name" ]; // 通过字符串使用KVC方式给属性赋值
stu1.nameLabel.text = @"张三" ;
[stu1 set Value:@"张三" for Key:@"nameLabel.text" ]; // 跨层赋值
2)、 KVO(key-Value-Observing):键值观察机制 他提供了观察某一属性变化的方法,极大的简化了代码。
KVO只能被KVC触发,包括使用setValue:forKey:方法和点语法。
KVC 和 KVO 的 keyPath 可以是属性、实例变量、成员变量。
####25、
KVC的底层实现?
当一个对象调用setValue方法时,方法内部会做以下操作:
1). 检查是否存在相应的key的set方法,如果存在,就调用set方法。
2). 如果set方法不存在,就会查找与key相同名称并且带下划线的成员变量,如果有,则直接给成员变量属性赋值。
3). 如果没有找到_key,就会查找相同名称的属性key,如果有就直接赋值。
4). 如果还没有找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。
这些方法的默认实现都是抛出异常,我们可以根据需要重写它们。
####26、
KVO的底层实现?
KVO基于runtime机制实现,KVO实际上是在原来的类的基础上写了一个隐藏的类别,我们使用的是这个看起来和原来类一样的隐藏的类别。
####27、
ViewController生命周期
按照执行顺序排列:
1)、 initWithCoder:通过nib文件初始化时触发。
2)、 awakeFromNib:nib文件被加载的时候,会发生一个awakeFromNib的消息到nib文件中的每个对象。
3)、loadView:开始加载视图控制器自带的view。
4)、viewDidLoad:视图控制器的view被加载完成。
5)、viewWillAppear:视图控制器的view将要显示在window上。
6)、updateViewConstraints:视图控制器的view开始更新AutoLayout约束。
7)、viewWillLayoutSubviews:视图控制器的view将要更新内容视图的位置。
8)、viewDidLayoutSubviews:视图控制器的view已经更新视图的位置。
9)、viewDidAppear:视图控制器的view已经展示到window上。
10)、viewWillDisappear:视图控制器的view将要从window上消失。
11)、viewDidDisappear:视图控制器的view已经从window上消失。
####28、
方法和选择器有何不同?
selector是一个方法的名字,方法是一个组合体,包含了名字和实现。
####29、
你是否接触过OC中的反射机制?简单聊一下概念和使用
1)、class反射
通过类名的字符串形式实例化对象:
Class class = NSClassFromString(@"student" );
Student *stu = [[class alloc] init];
将类名变为字符串:
Class class =[Student class];
NSString *className = NSStringFromClass(class);
2)、SEL的反射
通过方法的字符串形式实例化方法:
SEL selector = NSSelectorFromString(@"setName" );
[stu performSelector:selector withObject:@"Mike" ];
将方法变成字符串:
NSStringFromSelector(@selector*(set Name:));
####30、
调用方法有两种方式:
1). 直接通过方法名来调用。[person show];
2). 间接的通过SEL数据来调用
SEL aaa = @selector(show);
[person performSelector:aaa];
####31、
如何对iOS设备进行性能测试?
答: Profile-> Instruments ->Time Profiler
####32、
开发项目时你是怎么检查内存泄露?
1). 静态分析 analyze。
2). instruments工具里面有个leak可以动态分析。
####33、
什么是懒加载?
答:懒加载就是只在用到的时候才去初始化。也可以理解成延时加载。
我觉得最好也最简单的一个例子就是tableView中图片的加载显示了, 一个延时加载, 避免内存过高,一个异步加载,避免线程堵塞提高用户体验。
####34、
类变量的 @public,@protected,@private,@package 声明各有什么含义?
@public 任何地方都能访问;
@protected 该类和子类中访问,是默认的;
@private 只能在本类中访问;
@package 本包内使用,跨包不可以。
####35、
什么是谓词?
谓词就是通过NSPredicate给定的逻辑条件作为约束条件,完成对数据的筛选。
//定义谓词对象,谓词对象中包含了过滤条件(过滤条件比较多)
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d" ,30];
//使用谓词条件过滤数组中的元素,过滤之后返回查询的结果
NSArray *array = [persons filteredArrayUsingPredicate:predicate];
####36、
isa指针问题
isa:是一个Class 类型的指针. 每个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调 用时,先会从本身查找类方法的实现,如果没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向本身,这样形成了一个封闭的内循环。
####37、
如何访问并修改一个类的私有属性?
1)、 一种是通过KVC获取。
2)、通过runtime访问并修改私有属性。
####38、
一个objc对象的isa的指针指向什么?有什么作用?
答:指向他的类对象,从而可以找到对象上的方法。
####39、
下面的代码输出什么?
结论:都输出Son。super并不指向父类,只是告诉编译器要去父类中查找需要的内容
####40、
写一个完整的代理,包括声明、实现
####41、
isKindOfClass、isMemberOfClass、selector作用分别是什么
isKindOfClass
:作用是某个对象属于某个类型或者继承自某类型。
isMemberOfClass
:某个对象确切属于某个类型。
selector
:通过方法名,获取在内存中的函数的入口地址。
####42、
delegate 和 notification 的区别
1). 二者都用于传递消息,不同之处主要在于一个是一对一的,另一个是一对多的。
2). notification通过维护一个array,实现一对多消息的转发。
3). delegate需要两者之间必须建立联系,不然没法调用代理的方法;notification不需要两者之间有联系。
####43、
什么是block?
闭包(block):闭包就是获取其它函数局部变量的匿名函数。
####44、
block反向传值
在控制器间传值可以使用代理或者block,使用block相对来说简洁。
在前一个控制器的touchesBegan:方法内实现如下代码。