您的位置:首页 > 职场人生

IOS面试题

2016-03-16 15:34 441 查看

IOS面试

(一)

1.简述你对协议的理解:

a想做一件事却因为一些原因不能做,于是让b遵循a的一些约定,这样b就可以帮助a做事了。协议中包括了遵循方b需要实现的一些方法,以及b能够做的事情。

2.如何理解ARC自动引用计数机制:

ARC--自动引用计数 可以用来管理对象的使用以及了解对象的使用情况,当对象引用计数不为0时,对象不会被释放,当对象引用计数为0时 dealloc函数被调用 对象将会被释放。

3.如何理解retain/copy/assign/release/autorelease/dealloc关键字:

retain让对象引用计数加1,release让对象引用计数减1,当对象引用计数为0时会调用dealloc 将对象进行释放。copy是你不想让a和b共享一块内存时候,让a和b有各自的内存。在使用基本数据类型的时候需要使用assgin,assgin是直接赋值,会引起对象引用计数加1.

(1)assign:普通赋值,一般用于基本数据类型,防止循环引用。
(2)retain:保留计数,获得用户的所有权。
(3)copy:用来赋值对象,一般字符串使用copy,Foundation中的不可变对象使用copy效果相当于retain,只是引用计数+1.
(4)nonatomic:非原子性访问,不加同步,多线程并发访问会提高性能。


4.请简述类别和继承有什么联系和区别 以及weak和assgin的区别:

类别和继承 都会使用父类的中原有的方法和属性,类别是对父类进行扩展,继承是将父类中的属性、方法等保留下来,根据自己的实际情况进行实现。 weak用于各种UI控件和代理,assgin用于基本数据类型。

继承可以增加,修改,删除方法,还可以增加属性;

category只能添加属性;

strong:
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, strong) NSString *string2;
self.string1 = @"String 1";
self.string2 = self.string1;
self.string1 = nil;
NSLog(@"String 2 = %@", self.string2);
结果是:String 2 = String 1由于string2是strong定义的属性,所以引用计数+1,使得它们所指向的值都是@"String 1"
weak:
@property (nonatomic, strong) NSString *string1;
@property (nonatomic, weak) NSString *string2;
self.string1 = @"String 1";
self.string2 = self.string1;
self.string1 = nil;
NSLog(@"String 2 = %@", self.string2);
结果是:String 2 = null
分析一下,由于self.string1与self.string2指向同一地址,且string2没有retain内存地址,而self.string1=nil释放了内存,所以string1为nil。声明为weak的指针,指针指向的地址一旦被释放,这些指针都将被赋值为nil。这样的好处能有效的防止野指针。


5.请简述你对strong和weak关键字的理解 以及_unsafe_unretained与weak的区别:

strong相当于retain 让对象引用计数加1,防止对象在异常情况下被提前释放,导致crash。weak 弱引用防止产生循环应用无法释放对象和产生野指针。

strong叫做强引用,在ARC中使用strong告诉编译器帮组我们自动插入retain,weak是普通赋值相当于手动管理内存的assign。

_unsafe_unretained与weak功能一致,区别在于当指向的对象销毁后,weak会将变量重置为nil,防止调用野指针,产生EXC_BAD_ACCESS这类的错误。

6.如何实现ARC和MRC的混合编程:

在targets的build phases选项下Compile Sources下选择要使用arc编译的文件:  在targets的build phases选项下Compile Sources下选择要不使用arc编译的文件, 就会出报错。方法如下,双击它 输入 -fno-objc-arc 即可。

如果在ARC 工程中 加MRC 加 -fno-objc-arc 如果在MRC 工程中 加 ARC 加 -fobjc-arc

7.Objective-C是否支持多继承:

不支持多继承 因为消息机制名字查找发生在运行时而非编译时,很难解决多个基类可能导致的二义性问题 。可以通过消息转发 协议 类别实现与多继承相似的功能。

8.Objective-C中变量默认是私有的么?方法默认是私有的么?

变量默认是private,方法默认是public。

9.#import"".h 和@class+类名的区别:

import"".h为导入头文件,@class+类名 为前向引用申明。

1.import会包含这个类的所有信息,包括实体变量和方法,而@class只是告诉编译器,其后面声明的名称是类的名称,至于这些类是如何定义的,暂时不用考虑,后面会再告诉你。

2.在头文件中, 一般只需要知道被引用的类的名称就可以了。 不需要知道其内部的实体变量和方法,所以在头文件中一般使用@class来声明这个名称是类的名称。 而在实现类里面,因为会用到这个引用类的内部的实体变量和方法,所以需要使用#import来包含这个被引用类的头文件。

3.在编译效率方面考虑,如果你有100个头文件都#import了同一个头文件,或者这些文件是依次引用的,如A–>B, B–>C, C–>D这样的引用关系。当最开始的那个头文件有变化的话,后面所有引用它的类都需要重新编译,如果你的类有很多的话,这将耗费大量的时间。而是用@class则不会。

4.如果有循环依赖关系,如:A–>B, B–>A这样的相互依赖关系,如果使用#import来相互包含,那么就会出现编译错误,如果使用@class在两个类的头文件中相互声明,则不会有编译错误出现。

所以,一般来说,@class是放在interface中的,只是为了在interface中引用这个类,把这个类作为一个类型来用的。 在实现这个接口的实现类中,如果需要引用这个类的实体变量或者方法之类的,还是需要import在@class中声明的类进来.

10.请简述页面传值都有哪些实现方法:

Block、 委托协议、 通知、 单例、NSUserDefaults

11.请简述深拷贝和浅拷贝的区别:

深拷贝会重新在堆上开辟一块内存空间 是一个全新的对象 指针地址和原来不一样 浅拷贝不会重新开辟一块内存空间 指针和原来是一样的。

深拷贝和浅拷贝的本质是地址相同,就是浅拷贝,地址不同就是深拷贝。

浅拷贝是拷贝操作后,并没有进行真正的复制,而是另一个指针也指向了同一个地址。深拷贝操作后,是真正的复制了一份,另一个指针指向了拷贝后的地址。

浅拷贝:在复制操作时,对于被复制的对象的每一层复制都是指针复制。
深拷贝:在复制操作时,对于被复制的对象至少有一层复制是对象复制。 完全复制:在复制操作时,对于被复制的对象的每一层复制都是对象复制。

浅拷贝好比你的影子,你没了,你的影子也没有了;

深拷贝好比你的克隆人,你没了,你的克隆人还在 ;

备注:

retain:始终是浅复制。引用计数每次加一。返回对象是否可变与被复制的对象保持一致。


copy:对于可变对象为深复制,引用计数不改变;对于不可变对象是浅复制,引用计数每次加一。始终返回一个不可变对象。


mutableCopy:始终是深复制,引用计数不改变。始终返回一个可变对象。


不可变对象:值发生改变,其内存首地址随之改变。

可变对象:无论值是否改变,其内存首地址都不随之改变。

引用计数:为了让使用者清楚的知道,该对象有多少个拥有者(即有多少个指针指向同一内存地址)。

12.系统中有哪些对象是单例:

NSTimer、NSNotification、UIWindow、UIApplication

自己写单例需要注意:

不使用GCD:

// 单例模式实现要点:
// 1. 废掉构造方法(调用的时候抛出异常)
// 2. 提供一个类方法向外界返回该类的唯一实例
- (instancetype)init {
@throw [NSException exceptionWithName:@"CDSingleton" reason:@"不允许调用构造方法" userInfo:nil];
// return nil;
}
// 此方法由于没有在.h文件中暴露接口相当于是私有方法
- (instancetype) initPrivate {
if(self = [super init]) {
_value = arc4random();
}
return self;
}
+ (instancetype) sharedInstance {
// static类型的变量拥有全局的生命周期
static CDSingleton *instance = nil;
// 使用同步块保证在多线程环境下仍然是单例
//同步加锁,在多线程中使用,可以使线程安全
@synchronized(self) {
if(!instance) {
instance = [[self alloc] initPrivate];
}
}
return instance;
}

使用GCD:

+(instancetype)sharedInstance{
static HCDSingleton *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[HCDSingleton alloc]init];
});
return singleton;
}


13.简述你对MVC设计模式的理解:

MVC : Model - View - Controller 模型负责数据任务 试图负责呈现 以及和用于进行交互 控制器用来控制试图

model 模型:主要负责存储和操作数据
view 视图:主要负责展⽰示数据和⽤用户交互
controller 主要负责将model和view联系起来:
从网络获取数据->赋值给数据模型->将model的数据传递给view展⽰示(响应view的delegate和datasource⽅方法)->刷新view

14.IOS中那些技术符合观察者模式:

Key-Value Observing,它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。每次指定的被观察的对象的属性被修改后,KVO自动通知相应的观察者。

model中定义:
@interface StockData : NSObject { NSString * stockName; float price;}
@end
@implementation StockData
@end


controller中使用,这里相当于跟模型说,我要收听你的更新广播
- (void)viewDidLoad{
[super viewDidLoad];
stockForKVO = [[StockData alloc] init];
[stockForKVO setValue:@"searph" forKey:@"stockName"];
[stockForKVO setValue:@"10.0" forKey:@"price"];
[stockForKVO addObserver:self forKeyPath:@"price" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL];
myLabel = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 100, 30 )];
myLabel.textColor = [UIColor redColor];
myLabel.text = [stockForKVO valueForKey:@"price"];
[self.view addSubview:myLabel];
UIButton * b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
b.frame = CGRectMake(0, 0, 100, 30);
[b addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:b];
}

用户单击View中的button调用控制器中的action去更改模型中的数据
-(void) buttonAction{
[stockForKVO setValue:@"20.0" forKey:@"price"];
}

控制器需要实现的回调,相当于收到广播后我应该做啥事
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:@"price"])
{
myLabel.text = [stockForKVO valueForKey:@"price"];
}
}

注意使用通知和KVO一定要记着移除,否则会导致crash
视图dealloc需要取消观察
- (void)dealloc{
[super dealloc];
[stockForKVO removeObserver:self forKeyPath:@"price"]; [stockForKVO release];
}

15.简述你对工厂方法的理解:

创建对象的时候,我们一般是alloc一个对象,如果需要创建100个这样的对象,如果是在一个for循环中还好说,直接一句alloc就行了,但是事实并不那么如意,我们可能会在不同的地方去创建这个对象,那么我们可能需要写100句alloc 了,但是如果我们在创建对象的时候,需要在这些对象创建完之后,为它的一个属性添加一个固定的值,比方说都是某某学校的学生,那么可能有需要多些100行重复的代码了,那么,如果写一个-(void)createObj方法,把创建对象和学校属性写在这个方法里边,那么就是会省事很多,也就是说我们可以alloc
创建对象封装到一个方法里边,直接调用这个方法就可以了,这就是简单工厂方法。

16.什么是代理模式,实现代理需要注意什么:

代理模式:帮助别人完成别人委托你完成的事情。你需要注意遵守别人给你定的约定,和你是否拥有完成这件事的能力。

17.简述StoryBoard和Xib的联系和区别:

StoryBoard可以在上面实现整个项目界面的搭建,可以清楚得看到各个试图控制器之间的关系,XIB实现自定义要素和良好部件重用性复杂的UI。

18.请简述UITableView对CELL的重用机制:

比如一个屏幕可以放下5个UITableViewCell  总共会创建6个  设置CELL的重用标志符  一旦可以重用就重用  不能重用再创建,减少内存的消耗。
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellIdentifier = @"healthNewsTableViewCell";
healthNewsTableViewCell *cell = [myTableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = (healthNewsTableViewCell*)[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier: @"healthNewsTableViewCell"];
}
return cell;
}
//再将数据绑定写在WillDisPlayCell中
//让UITableView稍微顺滑点的方法  在显示cell前被调用
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
healthNewsTableViewCell *MyCell = (healthNewsTableViewCell *)cell;
MyCell.model = dataArray[indexPath.row];
MyCell.backgroundColor = [UIColor colorWithRed:0.936 green:0.941 blue:0.936 alpha:1.000];

}

19.如何使用UIScrollView实现无限加载多张图片:

写一个NSTimer 每隔0.5秒 执行以下回调方法changPic 如果图片数量有限 进行一个if判断 当超过图片总数时候,显示第一张,重新开始从第一张显示。

20.请简述试图控制器的生命周期:

当一个视图控制器被创建,并在屏幕上显示的时候。 代码的执行顺序
1、 alloc 创建对象,分配空间

2、init (initWithNibName) 初始化对象,初始化数据

3、loadView 从nib载入视图 ,通常这一步不需要去干涉。除非你没有使用xib文件创建视图

4、viewDidLoad 载入完成,可以进行自定义数据以及动态创建其他控件

5、viewWillAppear 视图将出现在屏幕之前,马上这个视图就会被展现在屏幕上了

6、viewDidAppear 视图已在屏幕上渲染完成

当一个视图被移除屏幕并且销毁的时候的执行顺序,这个顺序差不多和上面的相反
1、viewWillDisappear 视图将被从屏幕上移除之前执行

2、viewDidDisappear 视图已经被从屏幕上移除,用户看不到这个视图了

3、dealloc 视图被销毁,此处需要对你在init和viewDidLoad中创建的对象进行释放

21.UITableView有哪些优化方式:

1.图片使用异步加载如SDWebImage。

2.使用重用标识符reuseIdentifier,将重用标识标示符static NSString *CellIdentifier = @"XXX";

3.将数据绑定写在WillDisPlay这个协议方法中。

4.尽量在CELL中少使用不透明的View。

5.如果不是必须,减少使用reloadData全部cell。

22.简述IOS中的事件传递机制:

举例:如果view是控制器的view,就传递给控制器;如不是,则将其传递给它的父视图 在视图层次结构的最顶级视图,如果也不能处理收到的事件或消息,则其将事件或消息传递给window对象进行处理 如果window对象也不处理,则其将事件或消息传递给UIApplication对象 如果UIApplication也不能处理该事件或消息,则将其丢弃

注意:为什么用队列管理事件,而不用栈?
队列先进先出,能保证先产生的事件先处理。栈先进后出。

23.UITableView有哪些必须要实现的数据源方法:

// 获得section包含的cell个数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
//创建UITableViewCell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

//看情况实现的数据源方法:
//TableView中分组数目
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;
//分组头的标题
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;
//分组尾的标题
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;

个人习惯在此方法中实现给模型绑定数据
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath

24.简述Http协议中get请求和post请求的区别:

get 是从服务器获取数据 ,post是向服务器发送数据

GET安全性较低,POST安全性较高。因为GET在传输过程,数据被放在请求的URL中,而如今现有的很多服务器、代理服务器或者用户代理都会将请求URL记录到日志文件中,然后放在某个地方,这样就可能会有一些隐私的信息被第三方看到。另外,用户也可以在浏览器上直接看到提交的数据,一些系统内部消息将会一同显示在用户面前。POST的所有操作对用户来说都是不可见的。

备注:

GET请求,将参数直接写在访问路径上。操作简单,不过容易被外界看到,安全性不高,地址最多255字节;

POST请求,将参数放到body里面。POST请求操作相对复杂,需要将参数和地址分开,不过安全性高,参数放在body里面,不易被捕获。

25.简述你对异步请求数据的理解:

直白点 更好的用户体验: 减少卡顿 假死的现象。因为异步请求数据 不会一直等待某个任务完成才执行另外一个任务,可以执行其他的操作。

26.IOS中有哪些技术可以实现开辟线程,他们之间有什么联系:

由一个单例方法的实现推出多线程使用:

第一种,使用@synchronized(self)
static LocationController *sharedInstance;
+ (LocationController *)sharedInstance {
@synchronized(self)//使用一个同步保护 保证一个线程使用时,其他线程无法进入。
{
if (!sharedInstance)
sharedInstance=[[LocationController alloc] init];
}
return sharedInstance;
}

第二种,使用GCD(Grand Central Dispatch:宏中心派发)
当你调用dispatch_async,你通过一个dispatch队列,在这个队列上存有很多block,先进先出,依次执行。
这个队列可以使用dispatch_create自己创建,也可以调用主线程队列dispatch_get_main_queue。
这里建立的队列名称是onceToken。
static LocationController *sharedInstance;
+ (LocationController *)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!sharedInstance)
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}

第三种,使用NSOperationQueue
static LocationController *sharedInstance;
+ (LocationController *)sharedInstance {
NSOperationQueue *onceToken=[[NSOperationQueue alloc] init];
[onceToken addOperationWithBlock:^(){
if (!sharedInstance)
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}];
}

正式列举实现单例的常用的3种方法:

NSThread:

创建方式主要有两种:
//下面为两种NSThread的创建方法:
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];
NSThread* myThread = [[NSThread alloc] initWithTarget:selfselector:@selector(myThreadMainMethod:)object:nil];
[myThread start]; //启动线程

这两种方式的区别是:前一种一调用就会立即创建一个线程来做事情;而后一种虽然你 alloc 了也 init了,但是要直到我们手动调用 start 启动线程时才会真正去创建线程。这种延迟实现思想在很多跟资源相关的地方都有用到。后一种方式我们还可以在启动线程之前,对线程进行配置,比如设置 stack 大小,线程优先级。此外还有一种间接的方式:利用NSObject的方法performSelectorInBackground:withObject: 来创建一个线程:
[myObj performSelectorInBackground:@selector(myThreadMainMethod) withObject:nil];
//在后台运行某一个方法其效果与 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一样的。


NSOperationQueue (操作队列)

@interface ViewController () {
// 操作队列
NSOperationQueue *queue;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 并发队列(队列中的操作会并发执行)
queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 5;
for (int i = 0; i < 5; i++) {
CDCar *myCar = [[CDCar alloc] initWithX:20 + 70 * i andY:10];
[myCar draw:self.view];
NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[myCar run];
}];
[queue addOperation:op];
}
}
@end


GCD

dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 耗时的操作   比如网络数据数据请求
});
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});


27.NSThread中线程是如何实现通信的:

28.GCD中有哪些创建线程的方式:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 耗时的操作   比如网络数据数据请求
});
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});

29.IOS中哪些技术可以保证线程的安全:

1.使用互斥锁 互斥锁使用格式

@synchronized(锁对象)
{
// 需要锁定的代码
}
注意:锁定1份代码只用1把锁,用多把锁是无效的


互斥锁的优缺点

优点:能有效防止因多线程抢夺资源造成的数据安全问题

缺点:需要消耗大量的CPU资源

互斥锁的使用前提:多条线程抢夺同一块资源

相关专业术语:线程同步,多条线程按顺序地执行任务

互斥锁,就是使用了线程同步技术

OC在定义属性时有nonatomic和atomic两种选择

atomic:原子属性,为setter方法加锁(默认就是atomic)

nonatomic:非原子属性,不会为setter方法加锁

@property (assign, atomic) int age;
- (void)setAge:(int)age;
{
@synchronized(self)
{
_age = age;
}
}


原子和非原子属性的选择

nonatomic和atomic对比

atomic:线程安全,需要消耗大量的资源

nonatomic:非线程安全,适合内存小的移动设备

30.ASIHttpRequest的父类是什么:

ASIHTTPRequest是一款极其强劲的HTTP访问开源项目。让简单的API完成复杂的功能,

如:异步请求,队列请求,GZIP压缩,缓存,断点续传,进度跟踪,上传文件,HTTP认证。

它是NSOperationQueues的扩展,小而强大。
但也与它的父类略有区别。
如,仅添加到队列中其实并不能执行请求,只有调用[queue go]才会执行;
一个正在运行中的队列,并不需要重复调用[queue go ]。
默认情况下,队列中的一个请求如果失败,它会取消所有未完成的请求。
可以设置[queue setShouldCancel];


31.简述AFNetWork的实现原理:

AFNetwork是一个轻量级的网络请求api类库。是以NSURLConnection, NSOperation和其他方法为基础的。

AFNewWork的基本使用:

//创建一个下载任务
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://example.com/download.zip"];NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];

//创建一个上传任务
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Success: %@ %@", response, responseObject); }
}];
[uploadTask resume];

//带进度的上传任务
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue. // You are responsible for dispatching to the main queue for UI updates dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
[progressView setProgress:uploadProgress.fractionCompleted];
}); } completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
} }];
[uploadTask resume];

//请求数据的任务
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://httpbin.org/get"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}}];
[dataTask resume];


32.如何理解Block,block有什么用途:

Block是没有名字的匿名函数,类似于C语言中的函数指针。



可以用于页面间传值 进行回调...

举例:

//第二个页面:

.h
// 定义Block类型的属性(用来保存一段回调代码)
// 属性修饰符必须写copy因为要从栈将Block拷贝到堆上
@property (nonatomic, copy) void(^myBlock)(NSString *);

.m
- (IBAction)buttonClicked:(id)sender {
if (_myBlock) {
// 调用Block 语法跟调用C函数是一致的
_myBlock([NSString stringWithFormat:@"你好, %@", _nameField.text]);
}
[self dismissViewControllerAnimated:YES completion:^{
NSLog(@"回到第一个视图控制器!!!");
}];
}

//第一个页面 (接收页面)

.m
- (IBAction)buttonClicked:(UIButton *)sender {
CDSecondViewController *secondVC = [[CDSecondViewController alloc] init];
// 给第二个视图控制器的Block变量赋值
secondVC.myBlock = ^(NSString *str) {
_myLabel.text = str;
};
[self presentViewController:secondVC animated:YES completion:^{
// NSLog(@"搞定第二个视图控制器!!!");
}];
}

33.简述TCP和UDP的区别:

TCP传输可靠有保证,它有三次握手机制,这一机制保证校验了数据,保证了他的可靠性。而UDP就没有了,所以不可靠。不过UDP的速度是TCP比不了的,而且UDP的反应速度更快,QQ就是用UDP协议传输的,HTTP是用TCP协议传输的。

34.如何保证定位更省电:

desiredAccuracy,这个属性用来控制定位精度,精度越高耗电量越高,所以应该看实际情况设置这个属性的值。

35.简述SDWebImage的实现原理:

SDWebImage加载图片的流程:

1.入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片。
2.进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:。
3.先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
4.SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片。
5.如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存。
6.根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
7.如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
8.如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
9.共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
10.图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
11.connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
12.connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
13.图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
14.在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
15.imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
16.通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
17.将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
18.SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
19.SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
20.SDWebImagePrefetcher 可以预先下载图片,方便后续使用。

36.简述XML和JSON数据各有哪些优势:

(1).可读性方面。JSON和XML的数据可读性基本相同,JSON和XML的可读性可谓不相上下,一边是建议的语法,一边是规范的标签形式,XML可读性较好些。

(2).可扩展性方面。XML天生有很好的扩展性,JSON当然也有,没有什么是XML能扩展,JSON不能的。

(3).编码难度方面。XML有丰富的编码工具,比如Dom4j、JDom等,JSON也有json.org提供的工具,但是JSON的编码明显比XML容易许多,即使不借助工具也能写出JSON的代码,可是要写好XML就不太容易了。

(4).解码难度方面。XML的解析得考虑子节点父节点,让人头昏眼花,而JSON的解析难度几乎为0。这一点XML输的真是没话说。

(5).流行度方面。XML已经被业界广泛的使用,而JSON才刚刚开始,但是在Ajax这个特定的领域,未来的发展一定是XML让位于JSON。到时Ajax应该变成Ajaj(Asynchronous Javascript and JSON)了。

(6).解析手段方面。JSON和XML同样拥有丰富的解析手段。

(7).数据体积方面。JSON相对于XML来讲,数据的体积小,传递的速度更快些。

(8).数据交互方面。JSON与JavaScript的交互更加方便,更容易解析处理,更好的数据交互

(9).数据描述方面。JSON对数据的描述性比XML较差。

(10).传输速度方面。JSON的速度要远远快于XML。

37.简述线程和进程有什么联系和区别:

看到过一篇博客 博客上面,在这里引用博客中对线程和进程的理解:

1.计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。

2.假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。

3.进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。

4.一个车间里,可以有很多工人。他们协同完成一个任务。线程就好比车间里的工人。一个进程可以包括多个线程。

5.车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。

6.可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。

7.一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。

8.还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。

9.这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。

不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。

38.简述NSUserDefaults的使用场景和使用注意事项:

对于NSUerdefaults来说 一般可以用来保存用户的偏好设置 比如登陆账号 密码这些。 除此之外 我们还可以用它来保存图片, 字符串 , 数字 和对象。它被保存到项目中的Plists文件里面里。保存图片 一般用它里面的两个方法 图片保存可以用PNG或者JPG对应的方法 先转换成NSData 再用NSUerdefaults保存 保存的时候为了让它马上存下来要用synchronize 。它还可以用来在程序间进行反向传值。

39.IOS中数据库是用什么技术实现的:

Sqlite 3

40.简述什么是主键 什么是外键:

主键是用来唯一确定一条记录的唯一标记,如身份证号码。外键是用来关联其他表,可以通过这个外键唯一确定另外一张表上面的字段。如:A表中有个字段是B表中的主键,那么这个字段就是A的外键。

41.IOS中如何实现数据模型的存储:

42.简述IOS的沙盒机制:

IOS中的沙盒机制(SandBox)是一种安全体系,它规定了应用程序只能在为该应用创建的文件夹内读取文件,不可以访问其他地方的内容。所有的非代码文件都保存在这个地方,比如图片、声音、属性列表和文本文件等。

1.每个应用程序都在自己的沙盒内

2.不能随意跨越自己的沙盒去访问别的应用程序沙盒的内容

3.应用程序向外请求或接收数据都需要经过权限认证

43.如何实现真机调试:

Xcode7以后可以不用配置开发者证书直接进行真机测试 嘿嘿

44.如何查找项目中的内存泄露:

使用Instruments来查找程序中的内存泄露,以及NSZombieEnabled。

45.项目中支付环节如何实现的:

注意需要配置Apple ID 其他按照微信支付或者支付宝文档进行配置

46.如何实现项目上线到AppStore

47.简述你在项目中遇到过哪些问题:

以前做一个项目时,项目中使用了高德地图,当时时每点击进入一个单元格就进入地图,后来发现点击和退出的速度加快后,程序会crash。后来找了好久,发现 每次创建地图会占用很大的内存,而你每次还没有创建完毕就退出,就会导致崩溃,所以最后把创建地图的那一部分代码转移到了创建单元格哪里(前一个页面中),这样保证每次都只有一个地图。而不会因为地图还没有创建好就退出导致crash。

48.如何实现流媒体格式的视频边播放边缓存:

49.简述self.name = xxx 与_name=xxx的区别:

(1)self.name 有getter 和sett er 方法

(2)self.name 可以被kvo 监听到 (set 方法 和kvc方法)

(3)self.name 考虑了内存管理 是一个对象指针

前者是调用了setter方法,后者是普通的赋值。

50.#include与#import的区别、#import与@class的区别:

(1)#include与#import区别:#include和#import效果相同,只是后者不会引起交叉编译,确保头文件只会被导入一次。

(2)#import和@class的区别:import会导入类的所有信息,包括变量和方法,而@class只是告诉编译器,后面的名称是类的名称。推荐使用#import,编译效率更高,防止相互包含的编译错误。

(二)

1.IOS中是否支持垃圾回收机制:

IOS开发只支持手动内存管理和ARC,Mac开发支持GC垃圾回收机制,10.8之后弃用了GC,推荐使用ARC。

2.用户自定义了一个对象,如何实现拷贝(可变和不可变拷贝)

必须实现copying和mutableCopying协议,表示返回一个不可变和可变的对象。否则程序将会出现异常。

-(id)copyWithZone:(NSZone*)zone{
Person *person = [[self Class] allocWithZone:zone];
person->age = self.age;
person->name = self.name;
return person;
}

-(id)mutableCopyWithZone(NSZone*)zone;

3.释放对象的时候为什么要调用[super dealloc]:

因为子类继承父类,子类中一些对象(实例变量)也是继承父类的,因此我们需要调用父类的方法,将父类所拥有的实例进行释放。

4.简述常见的设计模式:

单例设计、代理设计、观察者(通知)、工厂方法。

5.内存管理中使用release方法好还是使用self.xxx = nil好:

使用self.xxx = nil 好,因为它会先调用release方法,而且将变量设置为nil,这样就可以更安全的释放对象,防止野指针调用。

6.事件响应者链的概念:

响应者链表示一系列的响应者对象。事件被交给第一响应者处理,如果第一响应者不处理,事件会沿着响应者链条向上传递,交给下一个响应者(next responder)。一般来说第一响应者是个视图或者其子类对象,当其被触摸后事件交给它处理,如果不处理,事件就会被传递到它的视图控制器(如果存在),然后是它的父视图(superview)(如果存在),以此类推,直到顶层视图。接下来会沿着顶层视图(top view)到窗口(UIWindow)再到UIApplication。如果整个过程都没有响应这个事件,该事件就会被丢弃。

7.static变量和static函数的区别:

(1)static变量表示静态存储变量,变量存储在静态存储区。

(2)加在函数前面表示该函数是内部连接,只在本文件中有效,别的文件中不能应用该函数。

static 全局变量与普通的全局变量有什么区别:static 全局变量只初使化一次,

防止在其他文件单元中被引用;

static 局部变量和普通局部变量有什么区别:static 局部变量只被初始化一次,

下一次依据上一次结果值;

static 函数与普通函数有什么区别:static 函数在内存中只有一份,普通函数

在每个被调用中维持一份拷贝

8.面向对象的三大支柱 以及如何理解动态绑定(多态):

三大支柱:封装、多态、继承

多态:

继承父类,实现方法,只看对象不看指针,不同的对象实现自己重写的不同的方法。

9.frame与bounds的区别:

(1)Frame:该view是在父view坐标系中的位置和大小(参照点是父视图的坐标系)当view做了transform时,该值不准确。

(2)Bounds:该view是在本身坐标系的位置和大小(参照点是本身的坐标系统)。

10.谈谈对Runloop的理解:

Run loop是线程相关的基础框架的一部分。一个run loop就是一个事件处理的循环,用来不停的调度工作以及处理输入事件。使用run loop的目的就是让你的线程在有工作的时候忙起来,没有工作的时候休眠。

11.SVN、Git协作开发,怎样防止代码文件冲突:

(1)防止代码冲突:不要同时多人修改同一个文件。例如A、B都同时修改一个文件,先让A修改,然后提交到服务器,然后B更新下来,在进行修改。

(2)服务器上的项目文件xcodeproj,仅让一个人管理提交,其他人只更新。防止此文件产生冲突。

12.断点续传如何实现的:

将下载的文件分成几个部分,通过http协议的请求头,设置每一部分下载的偏移量,然后通过多线程下载每一部分,下载完成以后 再组成为最终的完整文件。

13.堆和栈的区别 以及队列和栈的区别:

栈区(stack)由编译器自动分配释放,存放 方法(函数)的参数值、局部变量的值等、堆区(heap)一般由程序员分配和释放、若不释放,则内存溢出。

队列:先进先出 栈:先进后出

14.协议、代理的理解:

实际上是两个对象的相互调用

A类 B类 A中有B的对象 B.delegate=A

15.HTTP协议、TCP、UDP协议

数据请求:状态行、请求头、请求体

服务端响应:响应头(状态码:200;404资源没有找到;400客户端请求语法错误;500服务器错误;)响应体w3c school在线教程

16.IOS 6 与 IOS 7区别:

IOS 6 拟物化风格

IOS 7 扁平化风格

17.什么是指针:

type *p;

type:类数据类型。用来存储内存单元的编号指针 不完全等于 地址!有类型的标识很多类型的指针,指向数据、指向方法、void 型 。不同的编译环境,sizeof 可能不同。

18.写一个宏,输入两个参数返回其中较小的一个:

#define MIN(a,b) ((a)>(b)?(b):(a))

19.排序算法:

选择排序

-(void)bunbleSort:(NSMutableArray *)aData{
int count = 0;
for(int i = 0; i < [aData count]-1;i++)
{
for(int j = i+1; j < [aData count];j++)
{
if([[aData objectAtIndex:i] integerValue] < [[aDataobjectAtIndex:j]integerValue])
{
NSNumber *temp = [aData objectAtIndex:i];
[aData replaceObjectAtIndex:i withObject:[aData
objectAtIndex:j]];
[aData replaceObjectAtIndex:j withObject:temp];
count ++;}
}}
}

冒泡排序

-(void)sort2:(NSMutableArray *)resource{
//NSNumber小 -> 大
int count = [resource count];
for(int i = 0; i < count-1; i ++)
{
for(int j = 0; j < count-i-1; j ++)
{
if([[resource objectAtIndex:j] integerValue]>[[resource objectAtIndex:j+1] integerValue])
{
NSNumber *temp = [resource objectAtIndex:j];
[resource replaceObjectAtIndex:j
withObject:[resource objectAtIndex:j+1]];
[resource replaceObjectAtIndex:j+1
withObject:temp];
}
}}
}

快速排序

-(void)quickSortWithArray:(NSMutableArray *)aDataleft:(NSInteger)left right:(NSInteger)right
{
if (right > left) {NSInteger i = left;
NSInteger j = right + 1;
while (true) {
while (i+1 < [aData count] && [aData objectAtIndex:++i] <[aData objectAtIndex:left]) ;
while (j-1 > -1 && [aData objectAtIndex:--j] > [aDataobjectAtIndex:left]) ;
if (i >= j)
{
break;
}
[self swapWithData:aData index1:i index2:j];
}
[self swapWithData:aData index1:left index2:j];

[self quickSortWithArray:aData left:left right:j-1];
[self quickSortWithArray:aData left:j+1 right:right];
}
}
-(void)swapWithData:(NSMutableArray *)aDataindex1:(NSInteger)index1 index2:(NSInteger)index2
{
NSNumber *tmp = [aData objectAtIndex:index1];
[aData replaceObjectAtIndex:index1 withObject:[aDataobjectAtIndex:index2]];
[aData replaceObjectAtIndex:index2 withObject:tmp];
}

20.写一个单链表,要求可以插入数据和删除单个数据:

struct QFInfo{
int num;
struct QFInfo *next;
};
struct QFInfo *qfinfo;
//链表头
void insert_AtFirst(struct QFInfo *head,struct QFInfo *insert)
{
insert->next = head->next;
head->next = insert;
}
//链表尾
void insert_AtEnd(struct QFInfo *head,struct QFInfo *insert)
{
struct QFInfo *temp = head->next;
while (temp->next != NULL) {
temp = temp->next;
}
insert->next = NULL;
temp->next = insert;
}
//删除
void delete_1(struct QFInfo *head,struct QFInfo *del)
{
struct QFInfo *temp = head->next;
while (temp->next != NULL && temp->next != del)
{
temp = temp->next;
}
if(temp->next != NULL)
{
temp->next = temp->next->next;
}
}

21.下面的程序中,3次retainCount分别是什么,为什么:

NSMutableArray *arr = [[NSMutableArray array] retain];
NSString *str = [NSString stringWithFormat:@"test"];
[str retain];
[arr addObject:str];
NSLog(@"%@%lu",str,[str retainCount]);
[str retain];
[str release];
[str release];
NSLog(@"%@%lu",str,[str retainCount]);
[arr removeObject:str];
NSLog(@"%@%lu",str,[str retainCount]);
分别是3、2、1,因为初始化的时候retain  为1,retain的时候+1,往数组中add的时候+1,release的时候-1,从数组中删除的时候-1。

22.引用和指针的区别:

1 .从现象上看:指针在运行时可以改变其所指向的值,而引用一旦和某个对象绑定后就不再改变。引用访问一个变量是直接访问,而指针是间接访问。

2.从内存分配上看:程序为指针变量分配内存区域,而引用不分配内存区域。

3.从编译上看:程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能改。

23.如何将图片添加到相册:

UIImage *img = [UIImage imageWithNamed:@”123.ppng”];
UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil);

24.写一个获取日期的方法,输出格式为2016-08-08:

NSDate *date = [NSDate date];
NSDateFormatter *dateFm = [[NSDateFormatter alloc] init];
dateFm.dateFormat = @"yyyy-MM-dd";
NSString *dateStr = [dateFm stringFromDate:date];
NSLog(@"dateStr:%@",dateStr);

25.将一组数据永久保存到手机里:

NSUserDefaults、plist、数据库、普通文件、归档

26.NSarray *array = @[@“a”,@“b”,@“c”,@“a”,@“d”....],里面有N个string元素,求出array中唯一元素,要求复杂度为N:

(分析:将重复的元素去掉拼接成一个字符串)
代码实例:
NSArray *arr = @[@"a",@"b",@"b",@"c"];
NSString *resultStr = [arr objectAtIndex:0];
for (NSString *str in arr)
{
if ([resultStr rangeOfString:str].location == NSNotFound) {
resultStr = [resultStr stringByAppendingString:str];
}
}
NSLog(@"%@",resultStr);

27.写一个Block使用的例子,尽可能体现出block编程的语法和优势:

void(^myBlock)(NSString *msg);
myBlock = ^(NSString *str){
NSLog(@"%@",str);//line3
}
//使用block
//相当于把line3的代码放在这里执行。
myBlock(@"hello!");

28.nil和NULL有什么区别:

nil是一个对象,NULL是一个值

29.什么时候使用NSMutableArray,什么时候使用NSArray:

当数组在程序运行时,需要不断变化的,使用NSMutableArray,当数组在初始化后,便不再改变的,使用NSArray。需要指出的是,使用NSArray只表明的是该数组在运行时不发生改变,即不能往NSAarry的数组里新增和删除元素,但不表明其数组內的元素的内容不能发生改变。NSArray是线程安全的,NSMutableArray不是线程安全的,多线程使用到NSMutableArray需要注意。

30.如果我们不创建内存池,是否有内存池提供给我们:

界面线程维护自己的内存池,用户自己创建的数据线程,则需要创建该线程的内存池。

31.类NSObject的那些方法经常被使用:

NSObject是Objetive-C的基类,其由NSObject类及一系列协议构成。

其中类方法alloc、class、description 对象方法init、dealloc、– performSelector:withObject:afterDelay:等经常被使用。

32.什么是谓词:

谓词是通过NSPredicate,是通过给定的逻辑条件作为约束条件,完成对数据的筛选。
predicate = [NSPredicate predicateWithFormat:@"customerID == %d",n];
a = [customers filteredArrayUsingPredicate:predicate];

33.简述内存分区情况:

1).代码区:存放函数二进制代码

2).数据区:系统运行时申请内存并初始化,系统退出时由系统释放。存放全局变量、静态变量、常量

3).堆区:通过malloc等函数或new等操作符动态申请得到,需程序员手动申请和释放

4).栈区:函数模块内申请,函数结束时由系统自动释放。存放局部变量、函数参数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: