【2018年最新】 iOS面试题及答案(一)

一、设计模式是什么? 你知道哪些设计模式,并简要叙述?

设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。 1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。 2). MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。 3). 单例模式:经过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。 4). 观察者模式:KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。 5). 委托模式:代理+协议的组合。实现1对1的反向传值操做。 6). 工厂模式:经过一个类方法,批量的根据已有模板生产对象。算法

二、MVC 和 MVVM 的区别?

1). MVVM是对胖模型进行的拆分,其本质是给控制器减负,将一些弱业务逻辑放到VM中去处理。 2). MVC是一切设计的基础,全部新的设计模式都是基于MVC进行的改进。设计模式

三、#import跟 #include 有什么区别,@class呢,#import<> 跟 #import“”有什么区别?

答: 1). #import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入。 2). @class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,能够解决头文件的相互包含。 3). #import<>用来包含系统的头文件,#import””用来包含用户头文件。数组

四、frame 和 bounds 有什么不一样?

frame指的是:该view在父view坐标系统中的位置和大小。(参照点是父view的坐标系统) bounds指的是:该view在自己坐标系统中的位置和大小。(参照点是自己坐标系统)缓存

五、Objective-C的类能够多重继承么?能够实现多个接口么?Category是什么?重写一个类的方式用继承好仍是分类好?为何?

答:Objective-C的类不能够多重继承;能够实现多个接口(协议);Category是类别;通常状况用分类好,用Category去重写类的方法,仅对本Category有效,不会影响到其余类与原有类的关系。安全

六、@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的?

@property 的本质是什么? @property = ivar + getter + setter; “属性” (property)有两大概念:ivar(实例变量)、getter+setter(存取方法)bash

“属性” (property)做为 Objective-C 的一项特性,主要的做用就在于封装对象中的数据。 Objective-C 对象一般会把其所须要的数据保存为各类实例变量。实例变量通常经过“存取方法”(access method)来访问。其中,“获取方法” (getter)用于读取变量值,而“设置方法” (setter)用于写入变量值。服务器

七、@property中有哪些属性关键字?/ @property 后面能够有哪些修饰符?

属性能够拥有的特质分为四类: 1.原子性--- nonatomic 特质 2.读/写权限---readwrite(读写)、readonly (只读) 3.内存管理语义---assign、strong、 weak、unsafe_unretained、copy 4.方法名---getter= 、setter= 5.不经常使用的:nonnull,null_resettable,nullable微信

八、属性关键字 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,效率高。网络

九、什么状况使用 weak 关键字,相比 assign 有什么不一样?

1.在 ARC 中,在有可能出现循环引用的时候,每每要经过让其中一端使用 weak 来解决,好比: delegate 代理属性。 2.自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性通常也使用 weak;固然,也可使用strong。多线程

IBOutlet连出来的视图属性为何能够被设置成weak? 由于父控件的subViews数组已经对它有一个强引用。

不一样点: assign 能够用非 OC 对象,而 weak 必须用于 OC 对象。 weak 代表该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。

#####十、怎么用 copy 关键字?

用途:

  1. NSString、NSArray、NSDictionary 等等常用copy关键字,是由于他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary;
  2. block 也常用 copy 关键字。

说明: block 使用 copy 是从 MRC 遗留下来的“传统”,在 MRC 中,方法内部的 block 是在栈区的,使用 copy 能够把它放到堆区.在 ARC 中写不写都行:对于 block 使用 copy 仍是 strong 效果是同样的,但写上 copy 也无伤大雅,还能时刻提醒咱们:编译器自动对 block 进行了 copy 操做。若是不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操做”,他们有可能会在调用以前自行拷贝属性值。这种操做多余而低效。

#####十一、用@property声明的 NSString / NSArray / NSDictionary 常用 copy 关键字,为何?若是改用strong关键字,可能形成什么问题?

答:用 @property 声明 NSString、NSArray、NSDictionary 常用 copy 关键字,是由于他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操做(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无心间变更,应该在设置新属性值时拷贝一份。

  1. 由于父类指针能够指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 不管给我传入是一个可变对象仍是不可对象,我自己持有的就是一个不可变的副本。
  2. 若是咱们使用是 strong ,那么这个属性就有可能指向一个可变对象,若是这个可变对象在外部被修改了,那么会影响该属性。

//总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无心间篡改不可变类型对象原来的值。

#####十二、浅拷贝和深拷贝的区别?

答: 浅拷贝:只复制指向对象的指针,而不复制引用对象自己。 深拷贝:复制引用对象自己。内存中存在了两份独立对象自己,当修改A时,A_copy不变。

#####1三、系统对象的 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都是内容复制。用代码简单表示以下: NSString *str = @"hello word!"; NSString *strCopy = [str copy] // 指针复制,strCopy与str的地址同样 NSMutableString *strMCopy = [str mutableCopy] // 内容复制,strMCopy与str的地址不同

NSMutableString *mutableStr = [NSMutableString stringWithString: @"hello word!"];
NSString *strCopy = [mutableStr copy] // 内容复制
NSMutableString *strMCopy = [mutableStr mutableCopy] // 内容复制
复制代码

2、集合类对象的copy与mutableCopy (同上) 在集合类对象中,对不可变对象进行copy操做,是指针复制,mutableCopy操做是内容复制; 对可变对象进行copy和mutableCopy都是内容复制。可是:集合对象的内容复制仅限于对象自己,对集合内的对象元素仍然是指针复制。(即单层内容复制) NSArray *arr = @[@[@"a", @"b"], @[@"c", @"d"]; NSArray *copyArr = [arr copy]; // 指针复制 NSMutableArray *mCopyArr = [arr mutableCopy]; //单层内容复制

NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArr = [mutableArr copy]; // 单层内容复制
NSMutableArray *mCopyArr = [mutableArr mutableCopy]; // 单层内容复制
复制代码

【总结一句话】: 只有对不可变对象进行copy操做是指针复制(浅复制),其它状况都是内容复制(深复制)!

#####1四、这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr;?

问题:添加,删除,修改数组内的元素的时候,程序会由于找不到对应的方法而崩溃。 //如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460 // copy后返回的是不可变对象(即 arr 是 NSArray 类型,NSArray 类型对象不能调用 NSMutableArray 类型对象的方法) 缘由:是由于 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。

#####1五、如何让本身的类用 copy 修饰符?如何重写带 copy 关键字的 setter?

若想令本身所写的对象具备拷贝功能,则需实现 NSCopying 协议。若是自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。 具体步骤: 1. 需声明该类听从 NSCopying 协议 2. 实现 NSCopying 协议的方法。 // 该协议只有一个方法: - (id)copyWithZone:(NSZone *)zone; // 注意:使用 copy 修饰符,调用的是copy方法,其实真正须要实现的是 “copyWithZone” 方法。

#####1五、写一个 setter 方法用于完成 @property (nonatomic, retain) NSString *name,写一个 setter 方法用于完成 @property (nonatomic, copy) NSString *name ?

答: // retain

- (void)setName:(NSString *)str {
  [str retain];
  [_name release];
  _name = str;
}
复制代码

// copy

- (void)setName:(NSString *)str {
  id t = [str copy];
  [_name release];
  _name = t;
}
复制代码

#####1六、@synthesize 和 @dynamic 分别有什么做用?

@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。 若是@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var; // 在类的实现代码里经过 @synthesize 语法能够来指定实例变量的名字。(@synthesize var = _newVar;)

  1. @synthesize 的语义是若是你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
  2. @dynamic 告诉编译器,属性的setter与getter方法由用户本身实现,不自动生成(如,@dynamic var)。

#####1七、常见的 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。

#####1七、id 声明的对象有什么特性?

答:id 声明的对象具备运行时的特性,便可以指向任意类型的Objcetive-C的对象。

#####1九、Objective-C 如何对内存管理的,说说你的见解和解决方法?

答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。 1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。 2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。 3). 内存释放池Release Pool:把须要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中全部的内存空间也被自动释放掉。内存池的释放操做分为自动和手动。自动释放受runloop机制影响。

#####20、Objective-C 中建立线程的方法是什么?若是在主线程中执行代码,方法是什么?若是想延时执行代码、方法又是什么?

答:线程建立有三种方法:使用NSThread建立、使用GCD的dispatch、使用子类化的NSOperation,而后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,若是想延时执行代码能够用performSelector:onThread:withObject:waitUntilDone:

#####2一、Category(类别)、 Extension(扩展)和继承的区别

区别:

  1. 分类有名字,类扩展没有分类名字,是一种特殊的分类。
  2. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展能够扩展属性、成员变量和方法。
  3. 继承能够增长,修改或者删除方法,而且能够增长属性。

#####2二、咱们说的OC是动态运行时语言是什么意思?

答:主要是将数据类型的肯定由编译时,推迟到了运行时。简单来讲, 运行时机制使咱们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。

#####2三、为何咱们常见的delegate属性都用是week而不是retain/strong?

答:是为了防止delegate两端产生没必要要的循环引用。 @property (nonatomic, weak) id delegate;

#####2四、何时用delete,何时用Notification?

Delegate(委托模式):1对1的反向消息通知功能。 Notification(通知模式):只想要把消息发送出去,告知某些状态的变化。可是并不关心谁想要知道这个。

#####2五、什么是 KVO 和 KVC?

1). KVC(Key-Value-Coding):键值编码 是一种经过字符串间接访问对象的方式(即给属性赋值) 举例说明: stu.name = @"张三" // 点语法给属性赋值 [stu setValue:@"张三" forKey:@"name"]; // 经过字符串使用KVC方式给属性赋值 stu1.nameLabel.text = @"张三"; [stu1 setValue:@"张三" forKey:@"nameLabel.text"]; // 跨层赋值 2). KVO(key-Value-Observing):键值观察机制 他提供了观察某一属性变化的方法,极大的简化了代码。 KVO只能被KVC触发,包括使用setValue:forKey:方法和点语法。 // 经过下方方法为属性添加KVO观察

- (void)addObserver:(NSObject *)observer
                     forKeyPath:(NSString *)keyPath
                     options:(NSKeyValueObservingOptions)options
                     context:(nullable void *)context;
   // 当被观察的属性发送变化时,会自动触发下方方法                   
   - (void)observeValueForKeyPath:(NSString *)keyPath
                              ofObject:(id)object
                                  change:(NSDictionary *)change
                                 context:(void *)context{};

KVC 和 KVO 的 keyPath 能够是属性、实例变量、成员变量。
复制代码

#####2六、KVC的底层实现?

当一个对象调用setValue方法时,方法内部会作如下操做: 1). 检查是否存在相应的key的set方法,若是存在,就调用set方法。 2). 若是set方法不存在,就会查找与key相同名称而且带下划线的成员变量,若是有,则直接给成员变量属性赋值。 3). 若是没有找到_key,就会查找相同名称的属性key,若是有就直接赋值。 4). 若是尚未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。 这些方法的默认实现都是抛出异常,咱们能够根据须要重写它们。

#####2七、KVO的底层实现?

KVO基于runtime机制实现。

#####2八、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上消失。

#####2九、方法和选择器有何不一样?

selector是一个方法的名字,方法是一个组合体,包含了名字和实现。

#####30、你是否接触过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
(setName:));

#####3一、调用方法有两种方式:

1). 直接经过方法名来调用。[person show]; 2). 间接的经过SEL数据来调用 SEL aaa = @selector(show); [person performSelector:aaa];

#####3二、如何对iOS设备进行性能测试?

答: Profile-> Instruments ->Time Profiler

#####3三、开发项目时你是怎么检查内存泄露?

1). 静态分析 analyze。 2). instruments工具里面有个leak能够动态分析。

#####3四、什么是懒加载?

答:懒加载就是只在用到的时候才去初始化。也能够理解成延时加载。 我以为最好也最简单的一个例子就是tableView中图片的加载显示了, 一个延时加载, 避免内存太高,一个异步加载,避免线程堵塞提升用户体验。

#####3五、类变量的 @public,@protected,@private,@package 声明各有什么含义?

@public 任何地方都能访问; @protected 该类和子类中访问,是默认的; @private 只能在本类中访问; @package 本包内使用,跨包不能够。

#####3六、什么是谓词?

谓词就是经过NSPredicate给定的逻辑条件做为约束条件,完成对数据的筛选。 //定义谓词对象,谓词对象中包含了过滤条件(过滤条件比较多) NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d",30]; //使用谓词条件过滤数组中的元素,过滤以后返回查询的结果 NSArray *array = [persons filteredArrayUsingPredicate:predicate];

#####3七、isa指针问题

isa:是一个Class 类型的指针. 每一个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调 用时,先会从自己查找类方法的实现,若是没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向自己,这样造成了一个封闭的内循环。

#####3八、如何访问并修改一个类的私有属性?

1). 一种是经过KVC获取。 2). 经过runtime访问并修改私有属性。

#####3九、一个objc对象的isa的指针指向什么?有什么做用?

答:指向他的类对象,从而能够找到对象上的方法。

#####40、下面的代码输出什么?

@implementation Son : Father
- (id)init {
   if (self = [super init]) {
       NSLog(@"%@", NSStringFromClass([self class])); // Son
       NSLog(@"%@", NSStringFromClass([super class])); // Son
   }
   return self;
}
@end
复制代码

// 解析: self 是类的隐藏参数,指向当前调用方法的这个类的实例。 super是一个Magic Keyword,它本质是一个编译器标示符,和self是指向的同一个消息接收者。 不一样的是:super会告诉编译器,调用class这个方法时,要去父类的方法,而不是本类里的。 上面的例子无论调用[self class]仍是[super class],接受消息的对象都是当前 Son *obj 这个对象。

#####4一、写一个完整的代理,包括声明、实现

// 建立
@protocol MyDelagate
@required
-(void)eat:(NSString *)foodName; 
@optional
-(void)run;
@end

//  声明 .h
@interface person: NSObject<MyDelagate>

@end

//  实现 .m
@implementation person
- (void)eat:(NSString *)foodName { 
   NSLog(@"吃:%@!", foodName);
} 
- (void)run {
   NSLog(@"run!");
}

@end
复制代码

#####4二、isKindOfClass、isMemberOfClass、selector做用分别是什么

isKindOfClass:做用是某个对象属于某个类型或者继承自某类型。 isMemberOfClass:某个对象确切属于某个类型。 selector:经过方法名,获取在内存中的函数的入口地址。

#####4三、delegate 和 notification 的区别

1). 两者都用于传递消息,不一样之处主要在于一个是一对一的,另外一个是一对多的。 2). notification经过维护一个array,实现一对多消息的转发。 3). delegate须要二者之间必须创建联系,否则无法调用代理的方法;notification不须要二者之间有联系。

#####4四、什么是block?

闭包(block):闭包就是获取其它函数局部变量的匿名函数。

#####4五、block反向传值

在控制器间传值可使用代理或者block,使用block相对来讲简洁。

在前一个控制器的touchesBegan:方法内实现以下代码。

// OneViewController.m
  TwoViewController *twoVC = [[TwoViewController alloc] init];
  twoVC.valueBlcok = ^(NSString *str) {
  	NSLog(@"OneViewController拿到值:%@", str); 
  };
  [self presentViewController:twoVC animated:YES completion:nil];

  // TwoViewController.h   (在.h文件中声明一个block属性)
  @property (nonatomic ,strong) void(^valueBlcok)(NSString *str);

  // TwoViewController.m   (在.m文件中实现方法)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
	// 传值:调用block
	if (_valueBlcok) {
		_valueBlcok(@"123456");
	}
}
复制代码

#####4六、block的注意点

1). 在block内部使用外部指针且会形成循环引用状况下,须要用__week修饰外部指针: __weak typeof(self) weakSelf = self; 2). 在block内部若是调用了延时函数还使用弱指针会取不到该指针,由于已经被销毁了,须要在block内部再将弱指针从新强引用一下。 __strong typeof(self) strongSelf = weakSelf; 3). 若是须要在block内部改变外部栈区变量的话,须要在用__block修饰外部变量。

#####4七、BAD_ACCESS在什么状况下出现?

答:这种问题在开发时常常遇到。缘由是访问了野指针,好比访问已经释放对象的成员变量或者发消息、死循环等。

#####4八、lldb(gdb)经常使用的控制台调试命令?

1). p 输出基本类型。是打印命令,须要指定类型。是print的简写 p (int)[[[self view] subviews] count] 2). po 打印对象,会调用对象description方法。是print-object的简写 po [self view] 3). expr 能够在调试时动态执行指定表达式,并将结果打印出来。经常使用于在调试过程当中修改变量的值。 4). bt:打印调用堆栈,是thread backtrace的简写,加all可打印全部thread的堆栈 5). br l:是breakpoint list的简写

#####4九、你通常是怎么用Instruments的?

Instruments里面工具不少,经常使用: 1). Time Profiler: 性能分析 2). Zombies:检查是否访问了僵尸对象,可是这个工具只能从上往下检查,不智能。 3). Allocations:用来检查内存,写算法的那批人也用这个来检查。 4). Leaks:检查内存,看是否有内存泄露。

#####50、iOS中经常使用的数据存储方式有哪些?

数据存储有四种方案:NSUserDefault、KeyChain、file、DB。 其中File有三种方式:plist、Archive(归档) DB包括:SQLite、FMDB、CoreData

#####5一、iOS的沙盒目录结构是怎样的?

沙盒结构: 1). Application:存放程序源文件,上架前通过数字签名,上架后不可修改。 2). Documents:经常使用目录,iCloud备份目录,存放数据。(这里不能存缓存文件,不然上架不被经过) 3). Library: Caches:存放体积大又不须要备份的数据。(经常使用的缓存路径) Preference:设置目录,iCloud会备份设置信息。 4). tmp:存放临时文件,不会被备份,并且这个文件下的数据有可能随时被清除的可能。

#####5二、iOS多线程技术有哪几种方式?

答:pthread、NSThread、GCD、NSOperation

#####5三、GCD 与 NSOperation 的区别:

GCD 和 NSOperation 都是用于实现多线程: GCD 基于C语言的底层API,GCD主要与block结合使用,代码简洁高效。 NSOperation 属于Objective-C类,是基于GCD更高一层的封装。复杂任务通常用NSOperation实现。

#####5四、写出使用GCD方式从子线程回到主线程的方法代码

答:dispatch_sync(dispatch_get_main_queue(), ^{ });

如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,而后在都下载完成后合成一张整图)

// 使用Dispatch Group追加block到Global Group Queue,这些block若是所有执行完毕,就会执行Main Dispatch Queue中的结束处理的block。
// 建立队列组
dispatch_group_t group = dispatch_group_create();
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图片3 */ }); 
// 当并发队列组中的任务执行完毕后才会执行这里的代码
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合并图片
});
复制代码

#####5五、dispatch_barrier_async(栅栏函数)的做用是什么?

函数定义:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); 做用: 1.在它前面的任务执行结束后它才执行,它后面的任务要等它执行完成后才会开始执行。 2.避免数据竞争

// 1.建立并发队列

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向队列中添加任务
dispatch_async(queue, ^{  // 1.2是并行的
    NSLog(@"任务1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任务2, %@",[NSThread currentThread]);
});

dispatch_barrier_async(queue, ^{
    NSLog(@"任务 barrier, %@", [NSThread currentThread]);
});

dispatch_async(queue, ^{   // 这两个是同时执行的
    NSLog(@"任务3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"任务4, %@",[NSThread currentThread]);
});
复制代码

// 输出结果: 任务1 任务2 ——》 任务 barrier ——》任务3 任务4 // 其中的任务1与任务2,任务3与任务4 因为是并行处理前后顺序不定。

#####5六、如下代码运行结果如何?

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    NSLog(@"3");
}
// 只输出:1。(主线程死锁)
复制代码

#####5七、什么是 RunLoop

从字面上讲就是运行循环,它内部就是do-while循环,在这个循环内部不断地处理各类任务。 一个线程对应一个RunLoop,基本做用就是保持程序的持续运行,处理app中的各类事件。经过runloop,有事运行,没事就休息,能够节省cpu资源,提升程序性能。

主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个以下的main()函数

int main(int argc, char * argv[]) {
	@autoreleasepool {
    	return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
	}
}
复制代码

#####5八、什么是 Runtime

Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一,咱们平时编写的OC代码,底层都是基于它来实现的。

#####5九、Runtime实现的机制是什么,怎么用,通常用于干吗?

1). 使用时须要导入的头文件 <objc/message.h> <objc/runtime.h> 2). Runtime 运行时机制,它是一套C语言库。 3). 实际上咱们编写的全部OC代码,最终都是转成了runtime库的东西。 好比: 类转成了 Runtime 库里面的结构体等数据类型, 方法转成了 Runtime 库里面的C语言函数, 平时调方法都是转成了 objc_msgSend 函数(因此说OC有个消息发送机制) // OC是动态语言,每一个方法在运行时会被动态转为消息发送,即:objc_msgSend(receiver, selector)。 // [stu show]; 在objc动态编译时,会被转意为:objc_msgSend(stu, @selector(show)); 4). 所以,能够说 Runtime 是OC的底层实现,是OC的幕后执行者。

#####60、有了Runtime库,能作什么事情呢? Runtime库里面包含了跟类、成员变量、方法相关的API。 好比: (1)获取类里面的全部成员变量。 (2)为类动态添加成员变量。 (3)动态改变类的方法实现。 (4)为类动态添加新的方法等。 所以,有了Runtime,想怎么改就怎么改。

#####6一、什么是 Method Swizzle(黑魔法),什么状况下会使用?

1). 在没有一个类的实现源码的状况下,想改变其中一个方法的实现,除了继承它重写、和借助类别重名方法暴力抢先以外,还有更加灵活的方法 Method Swizzle。 2). Method Swizzle 指的是改变一个已存在的选择器对应的实现的过程。OC中方法的调用可以在运行时经过改变,经过改变类的调度表中选择器到最终函数间的映射关系。 3). 在OC中调用一个方法,实际上是向一个对象发送消息,查找消息的惟一依据是selector的名字。利用OC的动态特性,能够实如今运行时偷换selector对应的方法实现。 4). 每一个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点相似函数指针,指向具体的方法实现。 5). 咱们能够利用 method_exchangeImplementations 来交换2个方法中的IMP。 6). 咱们能够利用 class_replaceMethod 来修改类。 7). 咱们能够利用 method_setImplementation 来直接设置某个方法的IMP。 8). 归根结底,都是偷换了selector的IMP。

#####6二、_objc_msgForward 函数是作什么的,直接调用它将会发生什么?

答:_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并无实现的时候,_objc_msgForward会尝试作消息转发。

#####6三、什么是 TCP / UDP ?

TCP:传输控制协议。 UDP:用户数据协议。

TCP 是面向链接的,创建链接须要经历三次握手,是可靠的传输层协议。 UDP 是面向无链接的,数据传输是不可靠的,它只管发,无论收不收获得。 简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性通常。

#####6四、通讯底层原理(OSI七层模型)

OSI采用了分层的结构化技术,共分七层: 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

#####6五、介绍一下XMPP?

XMPP是一种以XML为基础的开放式实时通讯协议。 简单的说,XMPP就是一种协议,一种规定。就是说,在网络上传东西,XMM就是规定你上传大小的格式。

#####6六、OC中建立线程的方法是什么?若是在主线程中执行代码,方法是什么?

// 建立线程的方法
- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
- [self performSelectorInBackground:nil withObject:nil];
- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
- dispatch_async(dispatch_get_global_queue(0, 0), ^{});
- [[NSOperationQueue new] addOperation:nil];

// 主线程中执行代码的方法
- [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
- dispatch_async(dispatch_get_main_queue(), ^{});
- [[NSOperationQueue mainQueue] addOperation:nil];
复制代码

#####6七、tableView的重用机制?

答:UITableView 经过重用单元格来达到节省内存的目的: 经过为每一个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有能够重用的单元格,若是有,就拿过来用,若是没有就建立一个来使用。

#####6八、用伪代码写一个线程安全的单例模式

static id _instance;
+ (id)allocWithZone:(struct _NSZone *)zone {
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       _instance = [super allocWithZone:zone];
   });
   return _instance;
}

+ (instancetype)sharedData {
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       _instance = [[self alloc] init];
   });
   return _instance;
}

- (id)copyWithZone:(NSZone *)zone {
   return _instance;
}
复制代码

#####6九、如何实现视图的变形?

答:经过修改view的 transform 属性便可。

#####70、在手势对象基础类UIGestureRecognizer的经常使用子类手势类型中哪两个手势发生后,响应只会执行一次?

答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手势,手势发生后,响应只会执行一次。

#####7一、字符串经常使用方法:

NSString str = @"abc123"; NSArray arr = [str componentsSeparatedByString:@""]; //以目标字符串把原字符串分割成两部分,存到数组中。@[@"abc", @"123"];

#####7二、如何高性能的给 UIImageView 加个圆角?

很差的解决方案:使用下面的方式会强制Core Animation提早渲染屏幕的离屏绘制, 而离屏绘制就会给性能带来负面影响,会有卡顿的现象出现。

self.view.layer.cornerRadius = 5.0f; self.view.layer.masksToBounds = YES;

正确的解决方案:使用绘图技术

- (UIImage *)circleImage {
    // NO表明透明
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
    // 得到上下文
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    // 添加一个圆
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    CGContextAddEllipseInRect(ctx, rect);
    // 裁剪
    CGContextClip(ctx);
    // 将图片画上去
    [self drawInRect:rect];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    // 关闭上下文
    UIGraphicsEndImageContext();
    return image;
}
复制代码

还有一种方案:使用了贝塞尔曲线"切割"个这个图片, 给UIImageView 添加了的圆角,其实也是经过绘图技术来实现的。

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@"image"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
                       cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
复制代码

#####7三、你是怎么封装一个view的

1). 能够经过纯代码或者xib的方式来封装子控件 2). 创建一个跟view相关的模型,而后将模型数据传给view,经过模型上的数据给view的子控件赋值

/**
 *  纯代码初始化控件时必定会走这个方法
 */
- (instancetype)initWithFrame:(CGRect)frame {
    if(self = [super initWithFrame:frame]) {
        [self setupUI];
    }
    return self;
}

/**
 *  经过xib初始化控件时必定会走这个方法
 */
- (id)initWithCoder:(NSCoder *)aDecoder {
    if(self = [super initWithCoder:aDecoder]) {
        [self setupUI];
    }
    return self;
}

- (void)setupUI {
    // 初始化代码
}
复制代码

#####7四、HTTP协议中 POST 方法和 GET 方法有那些区别?

  1. GET用于向服务器请求数据,POST用于提交数据
  2. GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面,所以GET请求不适合用于验证密码等操做
  3. GET请求的URL有长度限制,POST请求不会有长度限制

#####7五、请简单的介绍下APNS发送系统消息的机制?

APNS优点:杜绝了相似安卓那种为了接受通知不停在后台唤醒程序保持长链接的行为,由iOS系统和APNS进行长链接替代。 APNS的原理: 1). 应用在通知中心注册,由iOS系统向APNS请求返回设备令牌(device Token) 2). 应用程序接收到设备令牌并发送给本身的后台服务器 3). 服务器把要推送的内容和设备发送给APNS 4). APNS根据设备令牌找到设备,再由iOS根据APPID把推送内容展现


qq技术交流群

qq交流群.jpeg
做者微信公众号
微信公众号.jpg
相关文章
相关标签/搜索