1.这段代码有什么问题?
for (int i = 0; i < 99999; i++) {
NSString *string = @"Abc";//常量区
string = [string lowercaseString];//新的堆区
string = [string stringByAppendingString:@"xyz"];//新的堆区
NSLog(@"%@", string);
}
解答:首先呢,这是一个循环,循环中,每次都定义一个常量string=“Abc”,然后返回小写字母,然后追加字符串@“xyz”,然后打印。输出结果呢,是99999个“abcxyz”。运行一遍后,发现没问题,没警告没crash。那么这段代码有什么问题呢?
问题1:如果临时变量string在循环内申请,那么循环没有意义。
问题2:在for循环中大量申请临时变量,在循环结束前是不释放的,这会造成内存的激增。当然了,string这个东西内存占用可以忽略,但是循环次数多了,还是很可怕的。想看到明显的效果,把string换成image试试?立竿见影。这种情况下就要使用自动释放池了.
for (int i = 0; i < 99999; i++) {
@autoreleasepool {
NSString *string = @"Abc";//常量区
string = [string lowercaseString];//新的堆区
string = [string stringByAppendingString:@"xyz"];//新的堆区
NSLog(@"%@", string);
}
}
扩展:什么情况下使用自动释放池 @autoreleasepool呢?
1、自动释放池使用的第一种情况 如果在一个循环内使用了大量的临时变量。需要使用自动释放池 来释放内存。
2、当你需要开辟一个子线程的时候,需要使用自动释放池。否则会出现内存溢出的情况
2.截取字符串"20 | http://www.baidu.com"
中,"|"字符前面和后面的数据,分别输出它们。
本题是考察基础类型的,很基础的一类题目。
NSString *string=@"20|http://www.baidu.com";
NSArray *array=[string componentsSeparatedByString:@"|"];
for (NSString *string in array) {
NSLog(@"%@",string);
}
3.用obj-c写一个冒泡排序
这是一个基础语法加上逻辑的题目
我们先申请一个数组
NSMutableArray *array=[[NSMutableArray alloc]initWithObjects:@"22",@"2",@"12",@"6",@"16",@"3", nil];
for(int i=0;i
这里要注意的是,我们要操作array进行替换的时候,不能用array[j]来替换,要用另外一个变量a来,因为替换后array就变了,array[j+1]可能就不等于b了
4.简述你对UIView和CALayer的理解
UIView和CALayer最终继承的都是NSObject
1、UIView的继承结构为UIView: UIResponder : NSObject。
UIResponder 是用来响应事件的,可见UIView是可以响应事件的。
2、CALayer的继承结构为:CALayer:NSObject。
CALayer直接父类是NSObject,中间并没有UIResponder,所以,CALayer不能响应事件。
CALayer定义了position、size、transform、animations 等基本属性。那UIView的size、frame、position这些属性是从那里来的呢?具体怎么样这里不说了,其实UIView是基于CALayer的高层封装,用来构造界面的。
UIView是用来显示内容的,可以处理用户事件;
CALayer是用来绘制内容的,对内容进行动画处理依赖与UIView来进行显示,不能处理用户事件。
那么,这是不两套结构体系呢?
并不是两套体系,UIView和CALayer是相互依赖的关系。UIView依赖与CALayer提供的内容,CALayer依赖UIView提供的容器来显示绘制的内容。归根到底CALayer是这一切的基础,如果没有CALayer,UIView自身也不会存在,UIView是一个特殊的CALayer实现,添加了响应事件的能力。
总的来说,他们两个是互相依存的关系,就相当于一幅画,画纸和颜料的关系。
5.对数组中的元素去重复
NSArray *array=@[@"aaa",@"bbb",@"ccc",@"aaa",@"ccc",@"ddd"];
思路1:重新构建一个新数组,然后遍历原来的数组,如果新数组没有这个元素,则加进去
NSMutableArray *secondArray = [[NSMutableArray alloc] initWithCapacity:array.count];
for (NSString *item in array) {
if (![secondArray containsObject:item]) {
[secondArray addObject:item];
}
}
NSLog(@"secondArray: %@", secondArray);
思路2:使用字典来进行去重。NSDictionary中的key-value,当key重复时候,不是新插入一个key,二十替换掉value的值。最后再转换成数组。当然,这个是无序的。
NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithCapacity:array.count];
for (NSString *item in array) {
[dic setObject:item forKey:item];
}
NSArray *secondArray = dic.allValues;
NSLog(@"%@", secondArray);
思路3:利用集合NSSet的特性(确定性、无序性、互异性),放入集合就自动去重了。当然,结果同样无序。
NSSet *set = [NSSet setWithArray:array];
NSArray *secondArray = [set allObjects];
NSLog(@"%@", secondArray);
6.元素的特性和作用
NSArray、NSSet、NSDictionary与NSMutableArray、NSMutableSet、NSMutableDictionary
特性:
NSArray表示不可变数组,是有序元素集,只能存储对象类型,可通过索引直接访问元素,而且元素类型可以不一样,但是不能进行增、删、改操作;NSMutableArray是可变数组,能进行增、删、改操作。通过索引查询值很快,但是插入、删除等效率很低。
NSSet表示不可变集合,具有确定性、互异性、无序性的特点,只能访问而不能修改集合;NSMutableSet表示可变集合,可以对集合进行增、删、改操作。集合通过值查询很快,插入、删除操作极快。
NSDictionary表示不可变字典,具有无序性的特点,每个key对应的值是唯一的,可通过key直接获取值;NSMutableDictionary表示可变字典,能对字典进行增、删、改操作。通过key查询值、插入、删除值都很快。
作用:
数组用于处理一组有序的数据集,比如常用的列表的dataSource要求有序,可通过索引直接访问,效率高。
集合要求具有确定性、互异性、无序性,在iOS开发中是比较少使用到的,比如枚举?
字典是键值对数据集,操作字典效率极高,时间复杂度为常量,但是值是无序的。在ios中,常见的JSON转字典,字典转模型就是其中一种应用。
7.简单描述一下XIB与Storyboards,说一下他们的优缺点
这种我们接触的比较多,初级的时候大部分都会用到这些。由于本人比较习惯于纯代码开发,很少使用SB和XIB,可能看法有些不正确,只代表个人观点,不存在xib好还是纯代码好的看法。
先说优点:
优点:
XIB:
在编译前就提供了可视化界面,可以直接拖控件,也可以直接给控件添加约束,更直观一些,而且类文件中就少了创建控件的代码,确实简化不少,通常每个XIB对应一个类。
直接的拖拽和自动约束,使之比较适合于新手使用,直观简洁,局部修改很方便。
Storyboard:
在编译前提供了可视化界面,可拖控件,可加约束,在开发时比较直观,而且一个storyboard可以有很多的界面,每个界面对应一个类文件,通过storybard,可以直观地看出整个App的结构。
缺点:
缺点:
XIB:
需求变动时,需要修改XIB很大,有时候甚至需要重新添加约束,导致开发周期变长。
XIB载入相比纯代码自然要慢一些。
对于比较复杂逻辑控制不同状态下显示不同内容时,使用XIB是比较困难的。
当多人团队或者多团队开发时,如果XIB文件被发动,极易导致冲突,而且解决冲突相对要困难很多。
Storyboard:
需求变动时,需要修改storyboard上对应的界面的约束,与XIB一样可能要重新添加约束,或者添加约束会造成大量的冲突,尤其是多团队开发。
对于复杂逻辑控制不同显示内容时,比较困难。
当多人团队或者多团队开发时,大家会同时修改一个storyboard,导致大量冲突,解决起来相当困难。
PS:当使用xib&storyboard开发的时候,一旦xib过多,而且你的MAC性能又不够优越的时候,你会体验到不一样的感觉。载入过慢?不不不,是非常非常慢。
8.请描述一下同步和异步,说说它们之间的区别。
首先,我们要明确一点,同步和异步都是在线程中使用的。在iOS开发中,比如网络请求数据时,若使用同步请求,则只有请求成功或者请求失败得到响应返回后,才能继续往下走,也就是才能访问其它资源(会阻塞了线程)。网络请求数据异步请求时,不会阻塞线程,在调用请求后,可以继续往下执行,而不用等请求有结果才能继续。
举个例子:
同步:
接力赛跑,二棒手一直在等一棒把棒传过来才能继续跑。当然,别抬杠,二棒手助跑那几米算是忽略的交互。
异步:
开会的时候,你打电话告诉助手把你的笔记本拿过来演示要说明的一个问题,助手去拿了,而你不用一直等他回来,你可以继续讲下一个问题。
区别:
线程同步:是多个线程访问同一资源时,只有当前正在访问的线程访问结束之后,其他线程才能开始访问(被阻塞)。
线程异步:是多个线程在访问竞争资源时,可以在空闲等待时去访问其它资源(不被阻塞)。
再举个例子:
你是一个面试官,只有你一个面试官。
同步:你一次只面试一个,外面有十个人在等,只有当前的一个面试好了,才能下一个。
异步:你让你面试的这个人做题,让他写出同步和异步的区别,他要思索和写花费十分钟时间,这段时间你不用一直看着他写,你可以去泡杯茶,去上个厕所。
9.描述一下iOS的内存管理,在开发中对于内存的使用和优化包含哪些方面。我们在开发中应该注意哪些问题。
谁强引用过,谁就要在不用的时候计数减1。
内存的使用和优化:
1.复用:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier,充分重用。
2.尽量把view设置为不透明:当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提高性能。
3.不要使用太复杂的XIB/Storyboard:载入时就会将XIB/storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才会使用。那些相比纯代码写的延迟加载,性能及内存就差了很多。
4.选择正确的数据结构:学会选择对业务场景最合适的数组结构是写出高效代码的基础。比如,数组: 有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。字典: 存储键值对,用键来查找比较快。集合: 无序的一组值,用值来查找很快,插入/删除很快。
5.延迟加载:对于不应该使用的数据,使用延迟加载方式。对于不需要马上显示的视图,使用延迟加载方式。比如,网络请求失败时显示的提示界面,可能一直都不会使用到,因此应该使用延迟加载。
6.数据缓存:对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于那些网络数据,不需要每次都请求的,应该缓存起来,可以写入数据库,也可以通过plist文件存储。
7.处理内存警告:一般在基类统一处理内存警告,将相关不用资源立即释放掉
8.重用大开销对象:一些objects的初始化很慢,比如NSDateFormatter和NSCalendar,但又不可避免地需要使用它们。通常是作为属性存储起来,防止反复创建。
9.避免反复处理数据:许多应用需要从服务器加载功能所需的常为JSON或者XML格式的数据。在服务器端和客户端使用相同的数据结构很重要。
10.使用Autorelease Pool:在某些循环创建临时变量处理数据时,自动释放池以保证能及时释放内存。
10.objc中的减号与加号代表什么?
前置加号(+)的方法为类方法,这类方法是可以直接用类名来调用的,它的作用主要是创建一个实例。
调用它的那个类中不需要再实例化一个变量了,也叫它静态方法。
前置减号(-)的方法为实例方法,必须使用类的实例才可以调用的。
11.什么是单例?单例的目的是什么?写出一个你熟悉的单例。
什么是单例,单例有什么用?
单例模式是一种常用的软件设计模式,保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。
熟悉的单例就比较多了。
UIApplication(应用程序实例)
NSNotificationCenter(消息中心):
NSFileManager(文件管理):
NSUserDefaults(应用程序设置):
NSURLCache(请求缓存):
12.什么是ARC
ARC是iOS 5推出的新功能,全称叫 ARC(Automatic Reference Counting)。
简单地说,就是代码中自动加入了retain/release,原先需要手动添加的用来处理内存管理的引用计数的代码可以自动地由编译器完成了。
该机能在 iOS 5/ Mac OS X 10.7 开始导入,利用 Xcode4.2 可以使用该机能。
简单地理解ARC,就是通过指定的语法,让编译器(LLVM 3.0)在编译代码时,自动生成实例的引用计数管理部分代码。
有一点,ARC并不是GC,它只是一种代码静态分析(Static Analyzer)工具。
那么,面试时候怎么回答呢?
好多人接触iOS时候就已经过了MRC的时代了,对于这个问题该怎么回答呢?
这个问题真正想了解的是对内存管理的理解,retain release虽然不用写了,但arc下还是会有内存泄漏野指针crash的bug存在。如果能从retain count这种内存管理策略的角度去阐述arc诞生的意义就算答对了。
13.#import 跟#include、@class有什么区别?#import<> 跟 #import""又什么区别?
#include是C中用来引用文件的关键字,而#import是obj-c中用来代替include的关键字。
#import可以确保同一个文件只能被导入一次,从而避免了使用#include容易引起的重复引用问题.
即classA引用了classC,classB也引用了classC,而当classD同时引用classA,classB的时候就会报重复引用的错误。
@class只是告诉编译器,后面遇到的这个名称是一个类名称,至于这个类是如何实现的暂不用考虑。
引入@class主要是用来解决引用死锁--如果两个类存在循环依赖关系,即A->B,B->A,如果用#import来相互包含,就会出现编译错误:
Expected specifier-qualifier-list before ‘A’或者Expected specifier-qualifier-list before ‘B’。
一般情况下,在 .h文件中,只需要知道类的名字就可以了,所以用@class
而在 .m文件中通常需要知道类的成员变量即方法,所以要用#import来将类文件导进来。
那为什么不在 .h文件中直接用#import来将类文件导入呢,因为如果导入大量的头文件,编译器就会花大量的时间来编译。
#import" "与#import<>:
#import" "实现从当前工作目录中找要导入的文件,如果没有再到系统类库中找.
而#import<>是直接从系统类库中找要导入的文件。
其中呢,#import"aaaa.h" 表示直接插入aaaa.h这个文件
#import则表示AAAA目录下的aaaa.h这个文件,比较类似于路径。
14.对于语句NSString*obj = [[NSData alloc] init]; ,编译时和运行时obj分别是什么类型?
#编译时是NSString类型;运行时是NSData类型
NSString*obj 告诉编译器,obj是个NSString 类型的指针,别管他后面有没有别的什么,反正告诉编译器了,obj就是个NSString,只能调用NSString的API。
[[NSData alloc] init] 。 NSData 开辟内存空间初始化,还是个NSData。
我们呢,可以这么理解,等号前面,申请了一个NSString类型的obj,然后初始化及赋值的时候给了一个NSData类型的,表面上他是个NSString,可内里却是个NSData。
使用的时候,obj可以使用NSString的所有API,xcode不会报错,还会自动提示补全API,但是运行的时候就会crash。
obj=[obj stringByAppendingString:@"aaa"];因为你在打代码的时候,编译器认为obj是个NSString,允许并主动配合提供API,
但是在运行的时候,obj是NSData类型的,调用的API在NSData的API里没有,所以引发错误。
15. 在一个对象的方法里面:self.name = @"object";和 _name = @"object"有什么不同吗?
self.name = @"object"; 是通过点语法修改属性name的值。
本质上使用的是name属性的setter方法进行的赋值操作,实际上执行的代码是
[self setName:@"object"];
例如:
@property(nonatomic, strong) NSString *name;
//根据@property关键词,系统自动生成setter方法。
- (void)setName:(NSString *)name{
//根据strong关键词
[name retain]; //内存计数+1
[_name release]; //把之前指针指向的内容内存计数-1
_name = name; //指向新内容
}
_name = @“object”; 只是单纯的把‘_name’指针指向‘@"object"’字符串对象所在的地址,没有调用方法。