目录html
1、基础知识点 2、第三方框架 3、算法 4、编码格式(优化细节) 5、其余知识点ios
本人精心整理了很全面iOS面试题资料,若是你想学想加入咱们请关注我私信“面试题”能够领取!!!记住必定要私信面试
1、基础知识点算法
设计模式是什么? 你知道哪些设计模式,并简要叙述?数据库
设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。 1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。 2). MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。 3). 单例模式:经过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。 4). 观察者模式:KVO是典型的观察者模式,观察某个属性的状态,状态发生变化时通知观察者。 5). 委托模式:代理+协议的组合。实现1对1的反向传值操做。 6). 工厂模式:经过一个类方法,批量的根据已有模板生产对象。编程
MVC 和 MVVM 的区别设计模式
MVVM是对胖模型进行的拆分,其本质是给控制器减负,将一些弱业务逻辑放到VM中去处理。 MVC是一切设计的基础,全部新的设计模式都是基于MVC进行的改进。 参考:iOS MVVM架构总结数组
#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(存取方法) “属性” (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 非原子操做。不写的话默认就是atomic。atomic 和 nonatomic 的区别在于,系统自动生成的 getter/setter 方法不同。对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操做的完整性,而nonatomic就没有这个保证了。因此,nonatomic的速度要比atomic快。 不过atomic可并不能保证线程安全。 参考:[爆栈热门 iOS 问题] atomic 和 nonatomic 有什么区别?
什么状况使用 weak 关键字,相比 assign 有什么不一样?
1.在 ARC 中,在有可能出现循环引用的时候,每每要经过让其中一端使用 weak 来解决,好比: delegate 代理属性。 2.自身已经对它进行一次强引用,没有必要再强引用一次,此时也会使用 weak,自定义 IBOutlet 控件属性通常也使用 weak;固然,也可使用strong。
IBOutlet连出来的视图属性为何能够被设置成weak? 由于父控件的subViews数组已经对它有一个强引用。
不一样点: assign 能够用非 OC 对象,而 weak 必须用于 OC 对象。 weak 代表该属性定义了一种“非拥有关系”。在属性所指的对象销毁时,属性值会自动清空(nil)。
怎么用 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,他们之间可能进行赋值操做(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无心间变更,应该在设置新属性值时拷贝一份。
总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无心间篡改不可变类型对象原来的值。
浅拷贝和深拷贝的区别?
浅拷贝:只复制指向对象的指针,而不复制引用对象自己。 深拷贝:复制引用对象自己。内存中存在了两份独立对象自己,当修改A时,A_copy不变。
系统对象的 copy 与 mutableCopy 方法
无论是集合类对象(NSArray、NSDictionary、NSSet ... 之类的对象),仍是非集合类对象(NSString, NSNumber ... 之类的对象),接收到copy和mutableCopy消息时,都遵循如下准则:
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操做是指针复制(浅复制),其它状况都是内容复制(深复制)!
这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray *arr;
问题:添加,删除,修改数组内的元素的时候,程序会由于找不到对应的方法而崩溃。 //如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460 // copy后返回的是不可变对象(即 arr 是 NSArray 类型,NSArray 类型对象不能调用 NSMutableArray 类型对象的方法) 缘由:是由于 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。
如何让本身的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
若想令本身所写的对象具备拷贝功能,则需实现 NSCopying 协议。若是自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。 具体步骤:
写一个 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; } @synthesize 和 @dynamic 分别有什么做用?
@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。 若是@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var; // 在类的实现代码里经过 @synthesize 语法能够来指定实例变量的名字。(@synthesize var = _newVar;)
常见的 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。
id 声明的对象有什么特性?
id 声明的对象具备运行时的特性,便可以指向任意类型的Objcetive-C的对象。
Objective-C 如何对内存管理的,说说你的见解和解决方法?
答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。 1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。 2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。 3). 内存释放池Release Pool:把须要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中全部的内存空间也被自动释放掉。内存池的释放操做分为自动和手动。自动释放受runloop机制影响。
Objective-C 中建立线程的方法是什么?若是在主线程中执行代码,方法是什么?若是想延时执行代码、方法又是什么?
答:线程建立有三种方法:使用NSThread建立、使用GCD的dispatch、使用子类化的NSOperation,而后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,若是想延时执行代码能够用performSelector:onThread:withObject:waitUntilDone:
Category(类别)、 Extension(扩展)和继承的区别
区别:
咱们说的OC是动态运行时语言是什么意思?
答:主要是将数据类型的肯定由编译时,推迟到了运行时。简单来讲, 运行时机制使咱们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
为何咱们常见的delegate属性都用是week而不是retain/strong?
答:是为了防止delegate两端产生没必要要的循环引用。 @property (nonatomic, weak) id delegate;
何时用delete,何时用Notification?
Delegate(委托模式):1对1的反向消息通知功能。 Notification(通知模式):只想要把消息发送出去,告知某些状态的变化。可是并不关心谁想要知道这个。
什么是 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观察
// 当被观察的属性发送变化时,会自动触发下方方法
KVC 和 KVO 的 keyPath 能够是属性、实例变量、成员变量。 iOS 成员变量,属性变量,局部变量,实例变量,全局变量 详解
KVC的底层实现?
当一个对象调用setValue方法时,方法内部会作如下操做: 1). 检查是否存在相应的key的set方法,若是存在,就调用set方法。 2). 若是set方法不存在,就会查找与key相同名称而且带下划线的成员变量,若是有,则直接给成员变量属性赋值。 3). 若是没有找到_key,就会查找相同名称的属性key,若是有就直接赋值。 4). 若是尚未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。 这些方法的默认实现都是抛出异常,咱们能够根据须要重写它们。
KVO的底层实现?
KVO基于runtime机制实现。 探究KVO的底层实现原理
ViewController生命周期
按照执行顺序排列:
方法和选择器有何不一样?
selector是一个方法的名字,方法是一个组合体,包含了名字和实现。
你是否接触过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:));
调用方法有两种方式:
1). 直接经过方法名来调用。[person show]; 2). 间接的经过SEL数据来调用 。SEL aaa = @selector(show); [person performSelector:aaa];
如何对iOS设备进行性能测试?
答: Profile-> Instruments ->Time Profiler
开发项目时你是怎么检查内存泄露?
1). 静态分析 analyze。 2). instruments工具里面有个leak能够动态分析。
什么是懒加载?
答:懒加载就是只在用到的时候才去初始化。也能够理解成延时加载。 我以为最好也最简单的一个例子就是tableView中图片的加载显示了, 一个延时加载, 避免内存太高,一个异步加载,避免线程堵塞提升用户体验。
类变量的 @public,@protected,@private,@package 声明各有什么含义?
@public 任何地方都能访问; @protected 该类和子类中访问,是默认的; @private 只能在本类中访问; @package 本包内使用,跨包不能够。
什么是谓词?
谓词就是经过NSPredicate给定的逻辑条件做为约束条件,完成对数据的筛选。 //定义谓词对象,谓词对象中包含了过滤条件(过滤条件比较多) NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d",30]; //使用谓词条件过滤数组中的元素,过滤以后返回查询的结果 NSArray *array = [persons filteredArrayUsingPredicate:predicate];
isa指针问题
isa:是一个Class 类型的指针. 每一个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调 用时,先会从自己查找类方法的实现,若是没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向自己,这样造成了一个封闭的内循环。
如何访问并修改一个类的私有属性?
1). 一种是经过KVC获取。 2). 经过runtime访问并修改私有属性。
一个objc对象的isa的指针指向什么?有什么做用?
答:指向他的类对象,从而能够找到对象上的方法。
下面的代码输出什么?
@implementation Son : Father
写一个完整的代理,包括声明、实现
// 建立 @protocol MyDelagate @required -(void)eat:(NSString *)foodName; @optional -(void)run; @end // 声明 .h @interface person: NSObject
@end // 实现 .m @implementation person - (void)eat:(NSString *)foodName { NSLog(@"吃:%@!", foodName); } - (void)run { NSLog(@"run!"); } @end
isKindOfClass、isMemberOfClass、selector做用分别是什么
isKindOfClass:做用是某个对象属于某个类型或者继承自某类型。 isMemberOfClass:某个对象确切属于某个类型。 selector:经过方法名,获取在内存中的函数的入口地址。
delegate 和 notification 的区别
1). 两者都用于传递消息,不一样之处主要在于一个是一对一的,另外一个是一对多的。 2). notification经过维护一个array,实现一对多消息的转发。 3). delegate须要二者之间必须创建联系,否则无法调用代理的方法;notification不须要二者之间有联系。
什么是block?
闭包(block):闭包就是获取其它函数局部变量的匿名函数。
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 *)touches withEvent:(UIEvent *)event { // 传值:调用block if (_valueBlcok) { _valueBlcok(@"123456"); } } block的注意点
1). 在block内部使用外部指针且会形成循环引用状况下,须要用__week修饰外部指针: __weak typeof(self) weakSelf = self; 2). 在block内部若是调用了延时函数还使用弱指针会取不到该指针,由于已经被销毁了,须要在block内部再将弱指针从新强引用一下。 __strong typeof(self) strongSelf = weakSelf; 3). 若是须要在block内部改变外部栈区变量的话,须要在用__block修饰外部变量。
BAD_ACCESS在什么状况下出现?
答:这种问题在开发时常常遇到。缘由是访问了野指针,好比访问已经释放对象的成员变量或者发消息、死循环等。
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的简写
你通常是怎么用Instruments的?
Instruments里面工具不少,经常使用: 1). Time Profiler: 性能分析 2). Zombies:检查是否访问了僵尸对象,可是这个工具只能从上往下检查,不智能。 3). Allocations:用来检查内存,写算法的那批人也用这个来检查。 4). Leaks:检查内存,看是否有内存泄露。
iOS中经常使用的数据存储方式有哪些?
数据存储有四种方案:NSUserDefault、KeyChain、File、DB。 其中File有三种方式:writeToFile:atomically:、Plist、NSKeyedAchiever(归档) DB包括:SQLite、FMDB、CoreData
iOS的沙盒目录结构是怎样的?
沙盒结构:
iOS多线程技术有哪几种方式?
答:pthread、NSThread、GCD、NSOperation
GCD 与 NSOperation 的区别:
GCD 和 NSOperation 都是用于实现多线程: GCD 基于C语言的底层API,GCD主要与block结合使用,代码简洁高效。 NSOperation 属于Objective-C类,是基于GCD更高一层的封装。复杂任务通常用NSOperation实现。
写出使用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(), ^{ // 合并图片 });
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 因为是并行处理前后顺序不定。
如下代码运行结果如何?
(void)viewDidLoad { [super viewDidLoad]; NSLog(@"1"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); }); NSLog(@"3"); } // 只输出:1。(主线程死锁) 什么是 RunLoop
从字面上看,就是运行循环,跑圈
其实它内部就是do-while循环,在这个循环内部不断地处理各类任务(好比Source、Timer、Observer)
一个线程对应一个RunLoop,基本做用就是保持程序的持续运行,处理app中的各类事件。
经过runloop,有事运行,没事就休息,能够节省cpu资源,提升程序性能。
主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个以下的main()函数 int main(int argc, char * argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } RunLoop学习总结
说说你对 runtime 的理解
Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一,咱们平时编写的OC代码,底层都是基于它来实现的。
Runtime实现的机制是什么,怎么用,通常用于干吗?
1). 使用时须要导入的头文件 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的幕后执行者。
有了Runtime库,能作什么事情呢? Runtime库里面包含了跟类、成员变量、方法相关的API。 好比: (1)获取类里面的全部成员变量。 (2)为类动态添加成员变量。 (3)为类动态添加新的方法。 (4)动态改变类的方法实现等。(Method Swizzling) 所以,有了Runtime,想怎么改就怎么改。 iOS:学习runtime的理解和心得
什么是 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。
_objc_msgForward 函数是作什么的,直接调用它将会发生什么?
答:_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并无实现的时候,_objc_msgForward会尝试作消息转发。
什么是 TCP / UDP ?
TCP:传输控制协议。 UDP:用户数据协议。
TCP 是面向链接的,创建链接须要经历三次握手,是可靠的传输层协议。 UDP 是面向无链接的,数据传输是不可靠的,它只管发,无论收不收获得。 简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性通常。
通讯底层原理(OSI七层模型)
OSI采用了分层的结构化技术,共分七层: 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
介绍一下XMPP?
XMPP是一种以XML为基础的开放式实时通讯协议。 简单的说,XMPP就是一种协议,一种规定。就是说,在网络上传东西,XMM就是规定你上传大小的格式。
OC中建立线程的方法是什么?若是在主线程中执行代码,方法是什么?
// 建立线程的方法 - [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
答:UITableView 经过重用单元格来达到节省内存的目的: 经过为每一个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有能够重用的单元格,若是有,就拿过来用,若是没有就建立一个来使用。
用伪代码写一个线程安全的单例模式
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; }
如何实现视图的变形?
答:经过修改view的 transform 属性便可。
在手势对象基础类UIGestureRecognizer的经常使用子类手势类型中哪两个手势发生后,响应只会执行一次?
答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手势,手势发生后,响应只会执行一次。
字符串经常使用方法:
NSString str = @"abc123"; NSArray arr = [str componentsSeparatedByString:@""]; //以目标字符串把原字符串分割成两部分,存到数组中。@[@"abc", @"123"];
如何高性能的给 UIImageView 加个圆角?
很差的解决方案:使用下面的方式会强制Core Animation提早渲染屏幕的离屏绘制, 而离屏绘制就会给性能带来负面影响
,会有卡顿的现象出现。 self.view.layer.cornerRadius = 5.0f; self.view.layer.masksToBounds = YES;
正确的解决方案:使用绘图技术
还有一种方案:使用了贝塞尔曲线"切割"个这个图片, 给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]; 你是怎么封装一个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 { // 初始化代码 }
HTTP协议中 POST 方法和 GET 方法有那些区别?
请简单的介绍下APNS发送系统消息的机制
APNS优点:杜绝了相似安卓那种为了接受通知不停在后台唤醒程序保持长链接的行为,由iOS系统和APNS进行长链接替代。 APNS的原理: 1). 应用在通知中心注册,由iOS系统向APNS请求返回设备令牌(device Token) 2). 应用程序接收到设备令牌并发送给本身的后台服务器 3). 服务器把要推送的内容和设备发送给APNS 4). APNS根据设备令牌找到设备,再由iOS根据APPID把推送内容展现
第一种:代理传值
第二个控制器: @protocol WJSecondViewControllerDelegate - (void)changeText:(NSString*)text; @end @property(nonatomic,assign)iddelegate;
第一个控制器:
第一个控制器: //注册监听通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitDataForModel:) name:@"NOV" object:nil];
第二个控制器: //发送通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"NOV" object:gameArray]; 第三种:单例传值
Single是一个单例类,而且有一个字符串类型的属性titleName 在第二个控制器:
第一个控制器:
第二个控制器: @property (nonatomic,copy) void (^changeText_block)(NSString*);
第一个控制器:
(IBAction)pushToSecond:(id)sender { WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil]; svc.str = self.navigationItem.title; [svc setChangeText_block:^(NSString *str) {
self.navigationItem.title = str; }]; [self.navigationController pushViewController:svc animated:YES]; } 第五种:extern传值
第二个控制器: extern NSString *btn;
第一个控制器: NSString *btn = nil;
第一个控制器:
第二个控制器:
Method1. performSelector方法
Method2. NSTimer定时器
Method3. NSThread线程的sleep
Method4. GCD
公用延迟执行方法
Method1: performSelector
[self performSelector:@selector(delayMethod) withObject:nil/可传任意类型参数/ afterDelay:2.0];` 注:此方法是一种非阻塞的执行方式,未找到取消执行的方法。
程序运行结束 2015-08-31 10:56:59.361 CJDelayMethod[1080:39604] delayMethodStart2015-08-31 10:56:59.363 CJDelayMethod[1080:39604] nextMethod2015-08-31 10:57:01.364 CJDelayMethod[1080:39604] delayMethodEnd Method2: NSTimer定时器
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];注:此方法是一种非阻塞的执行方式, 取消执行方法:
- (void)invalidate;`便可
程序运行结束 2015-08-31 10:58:10.182 CJDelayMethod[1129:41106] delayMethodStart2015-08-31 10:58:10.183 CJDelayMethod[1129:41106] nextMethod2015-08-31 10:58:12.185 CJDelayMethod[1129:41106] delayMethodEnd Method3: NSThread线程的sleep
[NSThread sleepForTimeInterval:2.0]; 注:此方法是一种阻塞执行方式,建议放在子线程中执行,不然会卡住界面。但有时仍是须要阻塞执行,如进入欢迎界面须要沉睡3秒才进入主界面时。 没有找到取消执行方式。
程序运行结束 2015-08-31 10:58:41.501 CJDelayMethod[1153:41698] delayMethodStart2015-08-31 10:58:43.507 CJDelayMethod[1153:41698] nextMethod Method4: GCD
__block ViewController/主控制器/ *weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/延迟执行时间/ * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [weakSelf delayMethod]; });
注:此方法能够在参数中选择执行的线程,是一种非阻塞执行方式。没有找到取消执行方式。
程序运行结束 2015-08-31 10:59:21.652 CJDelayMethod[1181:42438] delayMethodStart2015-08-31 10:59:21.653 CJDelayMethod[1181:42438] nextMethod2015-08-31 10:59:23.653 CJDelayMethod[1181:42438] delayMethodEnd 完整代码参见:
// > // ViewController.m > // CJDelayMethod > // > // Created by 陈杰 on 8/31/15. > // Copyright (c) 2015 chenjie. All rights reserved. > // >
import "ViewController.h" >
@interface ViewController () > @property (nonatomic, strong) NSTimer timer; @end > @implementation ViewController >
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"delayMethodStart");
[self methodOnePerformSelector];//
>[self methodTwoNSTimer];//
>[self methodThreeSleep];//
>[self methodFourGCD];
NSLog(@"nextMethod");
}
- (void)methodFiveAnimation{
[UIView animateWithDuration:0 delay:2.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ } completion:^(BOOL finished) {
[self delayMethod];
}];
}
- (void)methodFourGCD{
__block ViewController
weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[weakSelf delayMethod];
});
}
- (void)methodThreeSleep{
[NSThread sleepForTimeInterval:2.0];
}
- (void)methodTwoNSTimer{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];
}
- (void)methodOnePerformSelector{
[self performSelector:@selector(delayMethod) withObject:nil/*可传任意类型参数*/ afterDelay:2.0];
}
- (void)delayMethod{
NSLog(@"delayMethodEnd");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
>}
@end
NSPersistentStoreCoordinator , NSManaged0bjectContext 和NSManaged0bject中的那些须要在线程中建立或者传递
答:NSPersistentStoreCoordinator是持久化存储协调者,主要用于协调托管对象上下文和持久化存储区之间的关系。NSManagedObjectContext使用协调者的托管对象模型将数据保存到数据库,或查询数据。
您是否作过一部的网络处理和通信方面的工做?若是有,能具体介绍一下实现策略么?
答:使用NSOperation发送异步网络请求,使用NSOperationQueue管理线程数目及优先级,底层是用NSURLConnetion,
你使用过Objective-C的运行时编程(Runtime Programming)么?若是使用过,你用它作了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?
答:Objecitve-C的重要特性是Runtime(运行时),在#import 下能看到相关的方法,用过objc_getClass()和class_copyMethodList()获取过私有API;使用 Method method1 = class_getInstanceMethod(cls, sel1); Method method2 = class_getInstanceMethod(cls, sel2); method_exchangeImplementations(method1, method2);
代码交换两个方法,在写unit test时使用到。
Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG作过些什么动画或者图像上的内容。(有须要的话还能够涉及Quartz的一些内容)
答:UI框架的底层有CoreAnimation,CoreAnimation的底层有CoreGraphics。
| 目录
1、基础知识点 2、第三方框架 3、算法 4、编码格式(优化细节) 5、其余知识点
设计模式是什么? 你知道哪些设计模式,并简要叙述?
设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情。
1). MVC模式:Model View Control,把模型 视图 控制器 层进行解耦合编写。
2). MVVM模式:Model View ViewModel 把模型 视图 业务逻辑 层进行解耦和编写。
3). 单例模式:经过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。
4). 观察者模式:KVO是典型的观察者模式,观察某个属性的状态,状态发生变化时通知观察者。
5). 委托模式:代理+协议的组合。实现1对1的反向传值操做。
6). 工厂模式:经过一个类方法,批量的根据已有模板生产对象。
MVC 和 MVVM 的区别
MVVM是对胖模型进行的拆分,其本质是给控制器减负,将一些弱业务逻辑放到VM中去处理。
MVC是一切设计的基础,全部新的设计模式都是基于MVC进行的改进。
参考:iOS MVVM架构总结
#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(存取方法)
“属性” (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 非原子操做。不写的话默认就是atomic。atomic 和 nonatomic 的区别在于,系统自动生成的 getter/setter 方法不同。对于atomic的属性,系统生成的 getter/setter 会保证 get、set 操做的完整性,而nonatomic就没有这个保证了。因此,nonatomic的速度要比atomic快。
不过atomic可并不能保证线程安全。
参考:[爆栈热门 iOS 问题] 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 也[无伤大雅](https://www.baidu.com/s?wd=%E6%97%A0%E4%BC%A4%E5%A4%A7%E9%9B%85&tn=24004469_oem_dg&rsv_dl=gh_pl_sl_csd),还能时刻提醒咱们:编译器自动对 block 进行了 copy 操做。若是不写 copy ,该类的调用者有可能会忘记或者根本不知道“编译器会自动对 block 进行了 copy 操做”,他们有可能会在调用以前自行拷贝属性值。这种操做多余而低效。
> 答:用 @property 声明 NSString、NSArray、NSDictionary 常用 copy 关键字,是由于他们有对应的可变类型:NSMutableString、NSMutableArray、NSMutableDictionary,他们之间可能进行赋值操做(就是把可变的赋值给不可变的),为确保对象中的字符串值不会无心间变更,应该在设置新属性值时拷贝一份。 > > 1\. 由于父类指针能够指向子类对象,使用 copy 的目的是为了让本对象的属性不受外界影响,使用 copy 不管给我传入是一个可变对象仍是不可对象,我自己持有的就是一个不可变的副本。 > 2\. 若是咱们使用是 strong ,那么这个属性就有可能指向一个可变对象,若是这个可变对象在外部被修改了,那么会影响该属性。 > > 总结:使用copy的目的是,防止把可变类型的对象赋值给不可变类型的对象时,可变类型对象的值发送变化会无心间篡改不可变类型对象原来的值。
> 浅拷贝:只复制指向对象的指针,而不复制引用对象自己。 > 深拷贝:复制引用对象自己。内存中存在了两份独立对象自己,当修改A时,A_copy不变。
> 无论是集合类对象(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操做是指针复制(浅复制),其它状况都是内容复制(深复制)!
*这个写法会出什么问题:@property (nonatomic, copy) NSMutableArray arr;
问题:添加,删除,修改数组内的元素的时候,程序会由于找不到对应的方法而崩溃。
//如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460
// copy后返回的是不可变对象(即 arr 是 NSArray 类型,NSArray 类型对象不能调用 NSMutableArray 类型对象的方法)
缘由:是由于 copy 就是复制一个不可变 NSArray 的对象,不能对 NSArray 对象进行添加/修改。
如何让本身的类用 copy 修饰符?如何重写带 copy 关键字的 setter?
若想令本身所写的对象具备拷贝功能,则需实现 NSCopying 协议。若是自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying 与 NSMutableCopying 协议。
具体步骤:
1. 需声明该类听从 NSCopying 协议
2. 实现 NSCopying 协议的方法。
// 该协议只有一个方法:
- (id)copyWithZone:(NSZone *)zone;
// 注意:使用 copy 修饰符,调用的是copy方法,其实真正须要实现的是 “copyWithZone” 方法。
写一个 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; }
@synthesize 和 @dynamic 分别有什么做用?
@property有两个对应的词,一个是@synthesize(合成实例变量),一个是@dynamic。
若是@synthesize和@dynamic都没有写,那么默认的就是 @synthesize var = _var;
// 在类的实现代码里经过 @synthesize 语法能够来指定实例变量的名字。(@synthesize var = _newVar;)
1. @synthesize 的语义是若是你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。
2. @dynamic 告诉编译器,属性的setter与getter方法由用户本身实现,不自动生成(如,@dynamic var)。
常见的 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。
id 声明的对象有什么特性?
id 声明的对象具备运行时的特性,便可以指向任意类型的Objcetive-C的对象。
Objective-C 如何对内存管理的,说说你的见解和解决方法?
答:Objective-C的内存管理主要有三种方式ARC(自动内存计数)、手动内存计数、内存池。
1). 自动内存计数ARC:由Xcode自动在App编译阶段,在代码中添加内存管理代码。
2). 手动内存计数MRC:遵循内存谁申请、谁释放;谁添加,谁释放的原则。
3). 内存释放池Release Pool:把须要释放的内存统一放在一个池子中,当池子被抽干后(drain),池子中全部的内存空间也被自动释放掉。内存池的释放操做分为自动和手动。自动释放受runloop机制影响。
Objective-C 中建立线程的方法是什么?若是在主线程中执行代码,方法是什么?若是想延时执行代码、方法又是什么?
答:线程建立有三种方法:使用NSThread建立、使用GCD的dispatch、使用子类化的NSOperation,而后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,若是想延时执行代码能够用performSelector:onThread:withObject:waitUntilDone:
Category(类别)、 Extension(扩展)和继承的区别
区别:
1. 分类有名字,类扩展没有分类名字,是一种特殊的分类。
2. 分类只能扩展方法(属性仅仅是声明,并没真正实现),类扩展能够扩展属性、成员变量和方法。
3. 继承能够增长,修改或者删除方法,而且能够增长属性。
咱们说的OC是动态运行时语言是什么意思?
答:主要是将数据类型的肯定由编译时,推迟到了运行时。简单来讲, 运行时机制使咱们直到运行时才去决定一个对象的类别,以及调用该类别对象指定方法。
为何咱们常见的delegate属性都用是week而不是retain/strong?
答:是为了防止delegate两端产生没必要要的循环引用。
@property (nonatomic, weak) id delegate;
何时用delete,何时用Notification?
Delegate(委托模式):1对1的反向消息通知功能。
Notification(通知模式):只想要把消息发送出去,告知某些状态的变化。可是并不关心谁想要知道这个。
什么是 KVO 和 KVC?
> 1). KVC(Key-Value-Coding):键值编码 是一种经过字符串间接访问对象的方式(即给属性赋值) > 举例说明: > [stu.name](https://link.jianshu.com/?t=http%3A%2F%2Fstu.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 能够是属性、实例变量、成员变量。 > [iOS 成员变量,属性变量,局部变量,实例变量,全局变量 详解](https://link.jianshu.com/?t=http%3A%2F%2Fblog.csdn.net%2Fchenshun123%2Farticle%2Fdetails%2F52280564)
> 当一个对象调用setValue方法时,方法内部会作如下操做: > 1). 检查是否存在相应的key的set方法,若是存在,就调用set方法。 > 2). 若是set方法不存在,就会查找与key相同名称而且带下划线的成员变量,若是有,则直接给成员变量属性赋值。 > 3). 若是没有找到_key,就会查找相同名称的属性key,若是有就直接赋值。 > 4). 若是尚未找到,则调用valueForUndefinedKey:和setValue:forUndefinedKey:方法。 > 这些方法的默认实现都是抛出异常,咱们能够根据须要重写它们。
> KVO基于runtime机制实现。 > [探究KVO的底层实现原理](https://www.jianshu.com/p/829864680648)
> 按照执行顺序排列: > 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上消失。
> selector是一个方法的名字,方法是一个组合体,包含了名字和实现。
> 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:));
> 1). 直接经过方法名来调用。\[person show\]; > 2). 间接的经过SEL数据来调用 。SEL aaa = @selector(show); \[person performSelector:aaa\];
> 答: Profile-> Instruments ->Time Profiler
> 1). 静态分析 analyze。 > 2). instruments工具里面有个leak能够动态分析。
> 答:懒加载就是只在用到的时候才去初始化。也能够理解成延时加载。 > 我以为最好也最简单的一个例子就是tableView中图片的加载显示了, 一个延时加载, 避免内存太高,一个异步加载,避免线程堵塞提升用户体验。
> @public 任何地方都能访问; > @protected 该类和子类中访问,是默认的; > @private 只能在本类中访问; > @package 本包内使用,跨包不能够。
> 谓词就是经过NSPredicate给定的逻辑条件做为约束条件,完成对数据的筛选。 > //定义谓词对象,谓词对象中包含了过滤条件(过滤条件比较多) > NSPredicate *predicate = \[NSPredicate predicateWithFormat:@"age<%d",30\]; > //使用谓词条件过滤数组中的元素,过滤以后返回查询的结果 > NSArray *array = \[persons filteredArrayUsingPredicate:predicate\];
> isa:是一个Class 类型的指针. 每一个实例对象有个isa的指针,他指向对象的类,而Class里也有个isa的指针, 指向meteClass(元类)。元类保存了类方法的列表。当类方法被调 用时,先会从自己查找类方法的实现,若是没有,元类会向他父类查找该方法。同时注意的是:元类(meteClass)也是类,它也是对象。元类也有isa指针,它的isa指针最终指向的是一个根元类(root meteClass)。根元类的isa指针指向自己,这样造成了一个封闭的内循环。
> 1). 一种是经过KVC获取。 > 2). 经过runtime访问并修改私有属性。
> 答:指向他的类对象,从而能够找到对象上的方法。
> @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 这个对象。
// 建立 @protocol MyDelagate @required -(void)eat:(NSString *)foodName; @optional -(void)run; @end // 声明 .h @interface person: NSObject @end // 实现 .m @implementation person - (void)eat:(NSString *)foodName { NSLog(@"吃:%@!", foodName); } - (void)run { NSLog(@"run!"); } @end
isKindOfClass、isMemberOfClass、selector做用分别是什么
isKindOfClass:做用是某个对象属于某个类型或者继承自某类型。
isMemberOfClass:某个对象确切属于某个类型。
selector:经过方法名,获取在内存中的函数的入口地址。
delegate 和 notification 的区别
1). 两者都用于传递消息,不一样之处主要在于一个是一对一的,另外一个是一对多的。
2). notification经过维护一个array,实现一对多消息的转发。
3). delegate须要二者之间必须创建联系,否则无法调用代理的方法;notification不须要二者之间有联系。
什么是block?
闭包(block):闭包就是获取其它函数局部变量的匿名函数。
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 *)touches withEvent:(UIEvent *)event { // 传值:调用block if (_valueBlcok) { _valueBlcok(@"123456"); } }
block的注意点
1). 在block内部使用外部指针且会形成循环引用状况下,须要用__week修饰外部指针:
__weak typeof(self) weakSelf = self;
2). 在block内部若是调用了延时函数还使用弱指针会取不到该指针,由于已经被销毁了,须要在block内部再将弱指针从新强引用一下。
__strong typeof(self) strongSelf = weakSelf;
3). 若是须要在block内部改变外部栈区变量的话,须要在用__block修饰外部变量。
BAD_ACCESS在什么状况下出现?
答:这种问题在开发时常常遇到。缘由是访问了野指针,好比访问已经释放对象的成员变量或者发消息、死循环等。
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的简写
你通常是怎么用Instruments的?
Instruments里面工具不少,经常使用:
1). Time Profiler: 性能分析
2). Zombies:检查是否访问了僵尸对象,可是这个工具只能从上往下检查,不智能。
3). Allocations:用来检查内存,写算法的那批人也用这个来检查。
4). Leaks:检查内存,看是否有内存泄露。
iOS中经常使用的数据存储方式有哪些?
数据存储有四种方案:NSUserDefault、KeyChain、File、DB。
其中File有三种方式:writeToFile:atomically:、Plist、NSKeyedAchiever(归档)
DB包括:SQLite、FMDB、CoreData
iOS的沙盒目录结构是怎样的?
沙盒结构:
1. AppName.app 目录:这是应用程序的程序包目录,包含应用程序的自己。因为应用程序必须通过签名,因此您在运行时不能对这个目录中的内容进行修改,不然可能会使应用程序没法启动。
2. Documents:您应该将全部的应用程序数据文件写入到这个目录下。这个目录用于存储用户数据。iCloud备份目录。(这里不能存缓存文件,不然上架不被经过)
3. Library 目录:这个目录下有两个子目录:
Preferences 目录:包含应用程序的偏好设置文件。您不该该直接建立偏好设置文件,而是应该使用NSUserDefaults类来取得和设置应用程序的偏好.
Caches 目录:用于存放应用程序专用的支持文件,保存应用程序再次启动过程当中须要的信息。
可建立子文件夹。能够用来放置您但愿被备份但不但愿被用户看到的数据。该路径下的文件夹,除Caches之外,都会被iTunes备份。
4. tmp:存放临时文件,不会被备份,并且这个文件下的数据有可能随时被清除的可能。
iOS多线程技术有哪几种方式?
答:pthread、NSThread、GCD、NSOperation
GCD 与 NSOperation 的区别:
GCD 和 NSOperation 都是用于实现多线程:
GCD 基于C语言的底层API,GCD主要与block结合使用,代码简洁高效。
NSOperation 属于Objective-C类,是基于GCD更高一层的封装。复杂任务通常用NSOperation实现。
写出使用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(), ^{ > // 合并图片 > });
函数定义: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 因为是并行处理前后顺序不定。
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"1"); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); }); NSLog(@"3"); } // 只输出:1。(主线程死锁)
什么是 RunLoop
* 从字面上看,就是运行循环,跑圈
* 其实它内部就是do-while循环,在这个循环内部不断地处理各类任务(好比Source、Timer、Observer)
* 一个线程对应一个RunLoop,基本做用就是保持程序的持续运行,处理app中的各类事件。
* 经过runloop,有事运行,没事就休息,能够节省cpu资源,提升程序性能。
主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个以下的main()函数
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
RunLoop学习总结
说说你对 runtime 的理解
Runtime又叫运行时,是一套底层的C语言API,其为iOS内部的核心之一,咱们平时编写的OC代码,底层都是基于它来实现的。
Runtime实现的机制是什么,怎么用,通常用于干吗?
1). 使用时须要导入的头文件
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的幕后执行者。
有了Runtime库,能作什么事情呢?
Runtime库里面包含了跟类、成员变量、方法相关的API。
好比:
(1)获取类里面的全部成员变量。
(2)为类动态添加成员变量。
(3)为类动态添加新的方法。
(4)动态改变类的方法实现等。(Method Swizzling)
所以,有了Runtime,想怎么改就怎么改。
iOS:学习runtime的理解和心得
什么是 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。
_objc_msgForward 函数是作什么的,直接调用它将会发生什么?
答:_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并无实现的时候,_objc_msgForward会尝试作消息转发。
什么是 TCP / UDP ?
TCP:传输控制协议。
UDP:用户数据协议。
TCP 是面向链接的,创建链接须要经历三次握手,是可靠的传输层协议。
UDP 是面向无链接的,数据传输是不可靠的,它只管发,无论收不收获得。
简单的说,TCP注重数据安全,而UDP数据传输快点,但安全性通常。
通讯底层原理(OSI七层模型)
OSI采用了分层的结构化技术,共分七层:
物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
介绍一下XMPP?
XMPP是一种以XML为基础的开放式实时通讯协议。
简单的说,XMPP就是一种协议,一种规定。就是说,在网络上传东西,XMM就是规定你上传大小的格式。
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];
tableView的重用机制?
答:UITableView 经过重用单元格来达到节省内存的目的: 经过为每一个单元格指定一个重用标识符,即指定了单元格的种类,当屏幕上的单元格滑出屏幕时,系统会把这个单元格添加到重用队列中,等待被重用,当有新单元格从屏幕外滑入屏幕内时,从重用队列中找看有没有能够重用的单元格,若是有,就拿过来用,若是没有就建立一个来使用。
用伪代码写一个线程安全的单例模式
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; }
如何实现视图的变形?
答:经过修改view的 transform 属性便可。
在手势对象基础类UIGestureRecognizer的经常使用子类手势类型中哪两个手势发生后,响应只会执行一次?
答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手势,手势发生后,响应只会执行一次。
字符串经常使用方法:
NSString _str = @"abc_123";
NSArray arr = [str componentsSeparatedByString:@""]; //以目标字符串把原字符串分割成两部分,存到数组中。@[@"abc", @"123"];
如何高性能的给 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];
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 { // 初始化代码 }
HTTP协议中 POST 方法和 GET 方法有那些区别?
1. GET用于向服务器请求数据,POST用于提交数据
2. GET请求,请求参数拼接形式暴露在地址栏,而POST请求参数则放在请求体里面,所以GET请求不适合用于验证密码等操做
3. GET请求的URL有长度限制,POST请求不会有长度限制
请简单的介绍下APNS发送系统消息的机制
APNS优点:杜绝了相似安卓那种为了接受通知不停在后台唤醒程序保持长链接的行为,由iOS系统和APNS进行长链接替代。
APNS的原理:
1). 应用在通知中心注册,由iOS系统向APNS请求返回设备令牌(device Token)
2). 应用程序接收到设备令牌并发送给本身的后台服务器
3). 服务器把要推送的内容和设备发送给APNS
4). APNS根据设备令牌找到设备,再由iOS根据APPID把推送内容展现
第一种:代理传值
第二个控制器: @protocol WJSecondViewControllerDelegate - (void)changeText:(NSString*)text; @end @property(nonatomic,assign)iddelegate; - (IBAction)buttonClick:(UIButton*)sender { _str = sender.titleLabel.text; [self.delegate changeText:sender.titleLabel.text]; [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器: - (IBAction)pushToSecond:(id)sender { WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil]; svc.delegate = self; svc.str = self.navigationItem.title; [self.navigationController pushViewController:svc animated:YES]; [svc release]; } - (void)changeText:(NSString *)text{ self.navigationItem.title = text; }
第二种:通知传值
第一个控制器: //注册监听通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitDataForModel:) name:@"NOV" object:nil]; - (void)limitDataForModel:(NSNotification *)noti{ self.gamesInfoArray = noti.object; } 第二个控制器: //发送通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"NOV" object:gameArray];
第三种:单例传值
Single是一个单例类,而且有一个字符串类型的属性titleName 在第二个控制器: - (IBAction)buttonClick:(UIButton*)sender { Single *single = [Single sharedSingle]; single.titleName = sender.titleLabel.text; [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器: - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; Single *single = [Single sharedSingle]; self.navigationItem.title = single.titleName; }
第四种:block传值
第二个控制器: @property (nonatomic,copy) void (^changeText_block)(NSString*); - (IBAction)buttonClick:(UIButton*)sender { _str = sender.titleLabel.text; self.changeText_block(sender.titleLabel.text); [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器: - (IBAction)pushToSecond:(id)sender { WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil]; svc.str = self.navigationItem.title; [svc setChangeText_block:^(NSString *str) { >self.navigationItem.title = str; }]; [self.navigationController pushViewController:svc animated:YES]; }
第五种:extern传值
第二个控制器: extern NSString *btn; - (IBAction)buttonClick:(UIButton*)sender { btn = sender.titleLabel.text; [self.navigationController popViewControllerAnimated:YES]; } 第一个控制器: NSString *btn = nil; - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; self.navigationItem.title = btn; }
第六种:KVO传值
第一个控制器: - (void)viewDidLoad { [super viewDidLoad]; _vc =[[SecondViewController alloc]init]; //self监听vc里的textValue属性 [_vc addObserver:self forKeyPath:@"textValue" options:0 context:nil]; } 第二个控制器: - (IBAction)buttonClicked:(id)sender { self.textValue = self.textField.text; [self.navigationController popViewControllerAnimated:YES]; }
公用延迟执行方法
- (void)delayMethod{ NSLog(@"delayMethodEnd");
[self performSelector:@selector(delayMethod) withObject:nil/*可传任意类型参数*/ afterDelay:2.0];`
注:此方法是一种非阻塞的执行方式,未找到取消执行的方法。
> 程序运行结束
> 2015-08-31 10:56:59.361 CJDelayMethod[1080:39604] delayMethodStart2015-08-31 10:56:59.363 CJDelayMethod[1080:39604] nextMethod2015-08-31 10:57:01.364 CJDelayMethod[1080:39604] delayMethodEnd
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];` 注:此方法是一种非阻塞的执行方式, 取消执行方法:`- (void)invalidate;`便可 > 程序运行结束 > 2015-08-31 10:58:10.182 CJDelayMethod[1129:41106] delayMethodStart2015-08-31 10:58:10.183 CJDelayMethod[1129:41106] nextMethod2015-08-31 10:58:12.185 CJDelayMethod[1129:41106] delayMethodEnd
[NSThread sleepForTimeInterval:2.0];
注:此方法是一种阻塞执行方式,建议放在子线程中执行,不然会卡住界面。但有时仍是须要阻塞执行,如进入欢迎界面须要沉睡3秒才进入主界面时。
没有找到取消执行方式。
> 程序运行结束
> 2015-08-31 10:58:41.501 CJDelayMethod[1153:41698] delayMethodStart2015-08-31 10:58:43.507 CJDelayMethod[1153:41698] nextMethod
__block ViewController/*主控制器*/ *weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延迟执行时间*/ * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[weakSelf delayMethod];
});
注:此方法能够在参数中选择执行的线程,是一种非阻塞执行方式。没有找到取消执行方式。
> 程序运行结束
> 2015-08-31 10:59:21.652 CJDelayMethod[1181:42438] delayMethodStart2015-08-31 10:59:21.653 CJDelayMethod[1181:42438] nextMethod2015-08-31 10:59:23.653 CJDelayMethod[1181:42438] delayMethodEnd
完整代码参见: > // > // ViewController.m > // CJDelayMethod > // > // Created by 陈杰 on 8/31/15. > // Copyright (c) 2015 chenjie. All rights reserved. > // > > # import "ViewController.h" > > @interface ViewController () > @property (nonatomic, strong) NSTimer *timer; > @end > @implementation ViewController* > > *`- (void)viewDidLoad { `* > > *` [super viewDidLoad]; `* > > *` NSLog(@"delayMethodStart"); `* > > *` [self methodOnePerformSelector];// `* > > *` [self methodTwoNSTimer];// `* > > *` [self methodThreeSleep];//`* > > *` [self methodFourGCD]; `* > > *` NSLog(@"nextMethod");`* > > *`}`* > > *`- (void)methodFiveAnimation{ `* > > *` [UIView animateWithDuration:0 delay:2.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ } completion:^(BOOL finished) { `* > > *` [self delayMethod]; `* > > *` }];`* > > *`}` > `- (void)methodFourGCD{ `* > > *` __block ViewController`*`weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ ` > > ` [weakSelf delayMethod]; ` > > ` });` > > `}` > > `- (void)methodThreeSleep{ ` > > ` [NSThread sleepForTimeInterval:2.0];` > > `}` > > `- (void)methodTwoNSTimer{` > > ` NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];` > > `}` > > `- (void)methodOnePerformSelector{` > > ` [self performSelector:@selector(delayMethod) withObject:nil/*可传任意类型参数*/ afterDelay:2.0];` > > `}` > > `- (void)delayMethod{` > > ` NSLog(@"delayMethodEnd");` > > `}` > `- (void)didReceiveMemoryWarning { ` > > ` [super didReceiveMemoryWarning]; ` > > ` // Dispose of any resources that can be recreated.` > > `}` > > `@end`
NSPersistentStoreCoordinator , NSManaged0bjectContext 和NSManaged0bject中的那些须要在线程中建立或者传递
答:NSPersistentStoreCoordinator是持久化存储协调者,主要用于协调托管对象上下文和持久化存储区之间的关系。NSManagedObjectContext使用协调者的托管对象模型将数据保存到数据库,或查询数据。
您是否作过一部的网络处理和通信方面的工做?若是有,能具体介绍一下实现策略么?
答:使用NSOperation发送异步网络请求,使用NSOperationQueue管理线程数目及优先级,底层是用NSURLConnetion,
你使用过Objective-C的运行时编程(Runtime Programming)么?若是使用过,你用它作了什么?你还能记得你所使用的相关的头文件或者某些方法的名称吗?
答:Objecitve-C的重要特性是Runtime(运行时),在#import 下能看到相关的方法,用过objc_getClass()和class_copyMethodList()获取过私有API;使用
Method method1 = class_getInstanceMethod(cls, sel1);
Method method2 = class_getInstanceMethod(cls, sel2);
method_exchangeImplementations(method1, method2);
代码交换两个方法,在写unit test时使用到。
Core开头的系列的内容。是否使用过CoreAnimation和CoreGraphics。UI框架和CA,CG框架的联系是什么?分别用CA和CG作过些什么动画或者图像上的内容。(有须要的话还能够涉及Quartz的一些内容)
答:UI框架的底层有CoreAnimation,CoreAnimation的底层有CoreGraphics。
UIKit |
------------ |
Core Animation |
Core Graphics |
Graphics Hardware|
使用CA作过menu菜单的展开收起(太逊了)
是否使用过CoreText或者CoreImage等?若是使用过,请谈谈你使用CoreText或者CoreImage的体验。
答:CoreText能够解决复杂文字内容排版问题。CoreImage能够处理图片,为其添加各类效果。体验是很强大,挺复杂的。
自动释放池是什么,如何工做
答:当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放.它仍然是个OC的对象,所以自动释放池定义的做用域内的其它对象能够向它发送消息。当程序执行到做用域结束的位置时,自动释放池就会被释放,池中的全部对象也就被释放。
NSNotification和KVO的区别和用法是什么?何时应该使用通知,何时应该使用KVO,它们的实现上有什么区别吗?若是用protocol和delegate(或者delegate的Array)来实现相似的功能可能吗?若是可能,会有什么潜在的问题?若是不能,为何?(虽然protocol和delegate这种东西面试已经面烂了…)
答:NSNotification是通知模式在iOS的实现,KVO的全称是键值观察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一个经过属性名访问属性变量的机制。例如将Module层的变化,通知到多个Controller对象时,可使用NSNotification;若是是只须要观察某个对象的某个属性,可使用KVO。
对于委托模式,在设计模式中是对象适配器模式,其是delegate是指向某个对象的,这是一对一的关系,而在通知模式中,每每是一对多的关系。委托模式,从技术上能够如今改变delegate指向的对象,但不建议这样作,会让人迷惑,若是一个delegate对象不断改变,指向不一样的对象。
你用过NSOperationQueue么?若是用过或者了解的话,你为何要使用NSOperationQueue,实现了什么?请描述它和G.C.D的区别和相似的地方(提示:能够从二者的实现机制和适用范围来描述)。
答:使用NSOperationQueue用来管理子类化的NSOperation对象,控制其线程并发数目。GCD和NSOperation均可以实现对线程的管理,区别是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中使用NSOperation的优势是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具备面向对象的优势(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优势是GCD自己很是简单、易用,对于不复杂的多线程操做,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
既然提到G.C.D,那么问一下在使用G.C.D以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,须要注意些什么?
答:使用block是要注意,若将block作函数参数时,须要把它放到最后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block是闭包,是可以读取其余函数内部变量的函数。
对于Objective-C,你认为它最大的优势和最大的不足是什么?对于不足之处,如今有没有可用的方法绕过这些不足来实现需求。若是能够的话,你有没有考虑或者实践太重新实现OC的一些功能,若是有,具体会如何作?
> 答:最大的优势是它的运行时特性,不足是没有命名空间,对于命名冲突,可使用长命名法或特殊前缀解决,若是是引入的第三方库之间的命名冲突,可使用link命令及flag解决冲突。
> 答:抽象和封装,方便使用。首先是对问题有充分的了解,好比构建一个文件解压压缩框架,从使用者的角度出发,只需关注发送给框架一个解压请求,框架完成复杂文件的解压操做,而且在适当的时候通知给是哦难过者,如解压完成、解压出错等。在框架内部去构建对象的关系,经过抽象让其更为健壮、便于更改。其次是API的说明文档。
AFNetworking 底层原理分析
AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有如下类:
1). AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃)
2). AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。
3). AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变以后,这个工具类就能够检测到。
4). AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。
5). AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式
(AFJSONRequestSerializer).使用很少。
6). AFURLResponseSerialization:反序列化工具类;基类.使用比较多:
7). AFJSONResponseSerializer; JSON解析器,默认的解析器.
8). AFHTTPResponseSerializer; 万能解析器; JSON和XML以外的数据类型,直接返回二进
制数据.对服务器返回的数据不作任何处理.
9). AFXMLParserResponseSerializer; XML解析器;
描述下SDWebImage里面给UIImageView加载图片的逻辑
SDWebImage 中为 UIImageView 提供了一个分类UIImageView+WebCache.h, 这个分类中有一个最经常使用的接口sd_setImageWithURL:placeholderImage:,会在真实图片出现前会先显示占位图片,当真实图片被加载出来后再替换占位图片。
加载图片的过程大体以下:
1.首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 做为数据的索引先在内存中寻找是否有对应的缓存
2.若是缓存未找到就会利用经过MD5处理过的key来继续在磁盘中查询对应的数据, 若是找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来
3.若是在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片
4.下载后的图片会加入缓存中,并写入磁盘中
5.整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来
SDWebImage原理:
调用类别的方法:
1. 从内存(字典)中找图片(当这个图片在本次使用程序的过程当中已经被加载过),找到直接使用。
2. 从沙盒中找(当这个图片在以前使用程序的过程当中被加载过),找到使用,缓存到内存中。
3. 从网络上获取,使用,缓存到内存,缓存到沙盒。
友盟统计接口统计的全部功能
APP启动速度,APP停留页面时间等
1.不用中间变量,用两种方法交换A和B的值
// 1.中间变量 void swap(int a, int b) { int temp = a;
a = b;
b = temp;
} // 2.加法 void swap(int a, int b) {
a = a + b;
b = a - b;
a = a - b;
} // 3.异或(相同为0,不一样为1\. 能够理解为不进位加法) void swap(int a, int b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
2.求最大公约数
/** 1.直接遍历法 */ int maxCommonDivisor(int a, int b) { int max = 0; for (int i = 1; i <=b; i++) { if (a % i == 0 && b % i == 0) {
max = I;
}
} return max;
} /** 2.展转相除法 */ int maxCommonDivisor(int a, int b) { int r; while(a % b > 0) {
r = a % b;
a = b;
b = r;
} return b;
} // 扩展:最小公倍数 = (a * b)/最大公约数
3.模拟栈操做
/**
* 栈是一种数据结构,特色:先进后出
* 练习:使用全局变量模拟栈的操做
*/ #include #include #include //保护全局变量:在全局变量前加static后,这个全局变量就只能在本文件中使用 static int data[1024];//栈最多能保存1024个数据 static int count = 0;//目前已经放了多少个数(至关于栈顶位置) //数据入栈 push void push(int x){
assert(!full());//防止数组越界 data[count++] = x;
} //数据出栈 pop int pop(){
assert(!empty()); return data[--count];
} //查看栈顶元素 top int top(){
assert(!empty()); return data[count-1];
} //查询栈满 full bool full() { if(count >= 1024) { return 1;
} return 0;
} //查询栈空 empty bool empty() { if(count <= 0) { return 1;
} return 0;
} int main(){ //入栈 for (int i = 1; i <= 10; i++) {
push(i);
} //出栈 while(!empty()){ printf("%d ", top()); //栈顶元素 pop(); //出栈 } printf("\n"); return 0;
}
4.排序算法
选择排序、冒泡排序、插入排序三种排序算法能够总结为以下: * 都将数组分为已排序部分和未排序部分。 1\. 选择排序将已排序部分定义在左端,而后选择未排序部分的最小元素和未排序部分的第一个元素交换。 2\. 冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。 3\. 插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。
* 选择排序 /** * 【选择排序】:最值出如今起始端 * * 第1趟:在n个数中找到最小(大)数与第一个数交换位置 * 第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置 * 重复这样的操做...依次与第三个、第四个...数交换位置 * 第n-1趟,最终可实现数据的升序(降序)排列。 * / void selectSort(int *arr, int length) { for (int i = 0; i < length - 1; i++) { //趟数 for (int j = i + 1; j < length; j++) { //比较次数 if (arr[i] > arr[j]) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } * 冒泡排序 /* * 【冒泡排序】:相邻元素两两比较,比较完一趟,最值出如今末尾 * 第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n个元素位置 * 第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n-1个元素位置 * …… …… * 第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第2个元素位置 */ void bublleSort(int *arr, int length) { for(int i = 0; i < length - 1; i++) { //趟数 for(int j = 0; j < length - i - 1; j++) { //比较次数 if(arr[j] > arr[j+1]) { int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } }
参考:
8大排序算法图文讲解
经常使用算法OC实现
5.折半查找(二分查找)
/**
* 折半查找:优化查找时间(不用遍历所有数据)
*
* 折半查找的原理:
* 1> 数组必须是有序的
* 2> 必须已知min和max(知道范围)
* 3> 动态计算mid的值,取出mid对应的值进行比较
* 4> 若是mid对应的值大于要查找的值,那么max要变小为mid-1
* 5> 若是mid对应的值小于要查找的值,那么min要变大为mid+1
*
*/ // 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置 int findKey(int *arr, int length, int key) { int min = 0, max = length - 1, mid; while (min <= max) {
mid = (min + max) / 2; //计算中间值 if (key > arr[mid]) {
min = mid + 1;
} else if (key < arr[mid]) {
max = mid - 1;
} else { return mid;
}
} return -1;
}
参考:请Review下面的代码,并根据iOS的编码规范作出正确的修改
1.在 Objective-C 中,enum 建议使用NS_ENUM和NS_OPTIONS宏来定义枚举类型。
//定义一个枚举(比较严密) typedef NS_ENUM(NSInteger, BRUserGender) { BRUserGenderUnknown, // 未知 BRUserGenderMale, // 男性 BRUserGenderFemale, // 女性 BRUserGenderNeuter // 无性 }; @interface BRUser : NSObject @property (nonatomic, readonly, copy) NSString *name; @property (nonatomic, readonly, assign) NSUInteger age; @property (nonatomic, readonly, assign) BRUserGender gender; - (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender; @end //说明: //既然该类中已经有一个“初始化方法” ,用于设置 name、age 和 gender 的初始值: 那么在设计对应 @property 时就应该尽可能使用不可变的对象:其三个属性都应该设为“只读”。用初始化方法设置好属性值以后,就不能再改变了。 //属性的参数应该按照下面的顺序排列: (原子性,读写,内存管理)
2.避免使用C语言中的基本数据类型,建议使用 Foundation 数据类型,对应关系以下:
int -> NSInteger
unsigned -> NSUInteger
float -> CGFloat
动画时间 -> NSTimeInterval
HomeKit,是苹果2014年发布的智能家居平台。
什么是 OpenGL、Quartz 2D?
Quatarz 2d 是Apple提供的基本图形工具库。只是适用于2D图形的绘制。
OpenGL,是一个跨平台的图形开发库。适用于2D和3D图形的绘制。
ffmpeg框架:
ffmpeg 是音视频处理工具,既有音视频编码解码功能,又能够做为播放器使用。
谈谈 UITableView 的优化
1). 正确的复用cell;
2). 设计统一规格的Cell;
3). 提早计算并缓存好高度(布局),由于heightForRowAtIndexPath:是调用最频繁的方法;
4). 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口;
5). 滑动时按需加载,这个在大量图片展现,网络加载的时候很管用!
6). 减小子视图的层级关系;
7). 尽可能使全部的视图不透明化以及作切圆操做;
8). 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,而后经过hidden来控制是否显示;
9). 使用调试工具分析问题。
如何实行cell的动态的行高
若是但愿每条数据显示自身的行高,必须设置两个属性,1.预估行高,2.自定义行高。
设置预估行高 tableView.estimatedRowHeight = 200。
设置定义行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。
若是要让自定义行高有效,必须让容器视图有一个自下而上的约束。
说说你对 block 的理解
栈上的自动复制到堆上,block 的属性修饰符是 copy,循环引用的原理和解决方案。
block的循环引用;block的代码实现;为何会形成循环引用;block是如何强引用self的;
参考:Block 代码块中用到了self引用问题,致使循环引用
什么是野指针、空指针?
野指针:不知道指向了哪里的指针叫野指针。即指针指向不肯定,指针存的地址是一个垃圾值,未初始化。
空指针:不指向任何位置的指针叫空指针。即指针没有指向,指针存的地址是一个空地址,NULL。
什么是 OOA / OOD / OOP ?
OOA(Object Oriented Analysis) --面向对象分析
OOD(Object Oriented Design) --面向对象设计
OOP(Object Oriented Programming)--面向对象编程
多线程是什么
多线程是个复杂的概念,按字面意思是同步完成多项任务,提升了资源的使用效率,从硬件、操做系统、应用软件不一样的角度去看,多线程被赋予不一样的内涵,对于硬件,如今市面上多数的CPU都是多核的,多核的CPU运算多线程更为出色;从操做系统角度,是多任务,如今用的主流操做系统都是多任务的,能够一边听歌、一边写博客;对于应用来讲,多线程可让应用有更快的回应,能够在网络下载时,同时响应用户的触摸操做。在iOS应用中,对多线程最初的理解,就是并发,它的含义是原来先作烧水,再摘菜,再炒菜的工做,会变成烧水的同时去摘菜,最后去炒菜。
iOS 中的多线程
> iOS中的多线程,是Cocoa框架下的多线程,经过Cocoa的封装,可让咱们更为方便的使用线程,作过C++的同窗可能会对线程有更多的理解,好比线程的创立,信号量、共享变量有认识,Cocoa框架下会方便不少,它对线程作了封装,有些封装,可让咱们建立的对象,自己便拥有线程,也就是线程的对象化抽象,从而减小咱们的工程,提供程序的健壮性。 > > GCD是(Grand Central Dispatch)的缩写 ,从系统级别提供的一个易用地多线程类库,具备运行时的特色,能充分利用多核心硬件。GCD的API接口为C语言的函数,函数参数中多数有Block,关于Block的使用参看这里,为咱们提供强大的“接口”,对于GCD的使用参见本文 > > NSOperation与Queue > > NSOperation是一个抽象类,它封装了线程的细节实现,咱们能够经过子类化该对象,加上NSQueue来同面向对象的思惟,管理多线程程序。具体可参看这里:一个基于NSOperation的多线程网络访问的项目。 > > NSThread > > NSThread是一个控制线程执行的对象,它不如NSOperation抽象,经过它咱们能够方便的获得一个线程,并控制它。但NSThread的线程之间的并发控制,是须要咱们本身来控制的,能够经过NSCondition实现。 > > 参看 iOS多线程编程之NSThread的使用 > > 其余多线程 > > 在Cocoa的框架下,通知、Timer和异步函数等都有使用多线程,(待补充).
> 项目中使用NSOperation的优势是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具备面向对象的优势(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。 > > 项目中使用GCD的优势是GCD自己很是简单、易用,对于不复杂的多线程操做,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
> \* KVO就是cocoa框架实现的观察者模式,通常同KVC搭配使用,经过KVO能够监测一个值的变化,好比View的高度变化。是一对多的关系,一个值的变化会通知全部的观察者。 > \* NSNotification是通知,也是一对多的使用场景。在某些状况下,KVO和NSNotification是同样的,都是状态变化以后告知对方。NSNotification的特色,就是须要被观察者先主动发出通知,而后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,可是其优势是监听不局限于属性的变化,还能够对多种多样的状态变化进行监听,监听范围广,使用也更灵活。 > > \* delegate 是代理,就是我不想作的事情交给别人作。好比狗须要吃饭,就经过delegate通知主人,主人就会给他作饭、盛饭、倒水,这些操做,这些狗都不须要关心,只须要调用delegate(代理人)就能够了,由其余类完成所须要的操做。因此delegate是一对一关系。 > > \* block是delegate的另外一种形式,是函数式编程的一种形式。使用场景跟delegate同样,相比delegate更灵活,并且代理的实现更直观。 > > \* KVO通常的使用场景是数据,需求是数据变化,好比股票价格变化,咱们通常使用KVO(观察者模式)。delegate通常的使用场景是行为,需求是须要别人帮我作一件事情,好比买卖股票,咱们通常使用delegate。 > Notification通常是进行全局通知,好比利好消息一出,通知你们去买入。delegate是强关联,就是委托和代理双方互相知道,你委托别人买股票你就须要知道经纪人,经纪人也不要知道本身的顾客。Notification是弱关联,利好消息发出,你不须要知道是谁发的也能够作出相应的反应,同理发消息的人也不须要知道接收的人也能够正常发出消息。
* GCD方法,经过向主线程队列发送一个block块,使block里的方法能够在主线程中执行。 dispatch_async(dispatch_get_main_queue(), ^{ //须要执行的方法 }); * NSOperation 方法 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //主队列 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ //须要执行的方法 }]; [mainQueue addOperation:operation]; * NSThread 方法 [self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil]; [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES]; [[NSThread mainThread] performSelector:@selector(method) withObject:nil]; * RunLoop方法 [[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];
如何让计时器调用一个类方法
* 计时器只能调用实例方法,可是能够在这个实例方法里面调用静态方法。
* 使用计时器须要注意,计时器必定要加入RunLoop中,而且选好model才能运行。scheduledTimerWithTimeInterval方法建立一个计时器并加入到RunLoop中因此能够直接使用。
* 若是计时器的repeats选择YES说明这个计时器会重复执行,必定要在合适的时机调用计时器的invalid。不能在dealloc中调用,由于一旦设置为repeats 为yes,计时器会强持有self,致使dealloc永远不会被调用,这个类就永远没法被释放。好比能够在viewDidDisappear中调用,这样当类须要被回收的时候就能够正常进入dealloc中了。
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES]; -(void)timerMethod { //调用类方法 [[self class] staticMethod]; } -(void)invalid { [timer invalid]; timer = nil; }
如何重写类方法
一、在子类中实现一个同基类名字同样的静态方法
二、在调用的时候不要使用类名调用,而是使用[self class]的方式调用。原理,用类名调用是早绑定,在编译期绑定,用[self class]是晚绑定,在运行时决定调用哪一个方法。
NSTimer建立后,会在哪一个线程运行
用scheduledTimerWithTimeInterval建立的,在哪一个线程建立就会被加入哪一个线程的RunLoop中就运行在哪一个线程
本身建立的Timer,加入到哪一个线程的RunLoop中就运行在哪一个线程。
id和NSObject*的区别
typedef struct objc_object *id
id能够理解为指向对象的指针。全部oc的对象 id均可以指向,编译器不会作类型检查,id调用任何存在的方法都不会在编译阶段报错,固然若是这个id指向的对象没有这个方法,该崩溃仍是会崩溃的。
NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法不然就要作强制类型转换。
不是全部的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject *可指向的类型是id的子集。
static关键字的做用
回答一:
1.在函数体内定义的static他的做用域为该函数体,该变量在内存中只被分配一次,所以,其值在下次调用时仍维持上次的值不变;
2.在模块内的static全局变量能够被模块内全部函数访问,可是不能被模块外的其余函数访问;
3.在模块内的staic全局变量能够被这一模块内的其余函数调用,这个函数的使用范围被限制在这个模块内;
4.在类中的static成员变量属于整个类所拥有,对类的全部对象只有一份拷贝,也就是说只要是该类的对象,那么该对象的中被static修饰的成员变量都指向同一块地址。
回答二:
修饰局部变量:
1.延长局部变量的生命周期,程序结束才会销毁。
2.局部变量只会生成一分内存,只会初始化一次。
3.改变局部变量的做用域。
修饰全局变量:
1.只能在本文件中访问,修改全局变量的做用域,生命周期不会改
2.避免重复定义全局变量
在OC中static关键字使用误区
1.使用static修饰实例变量是不被容许的
2.使用static修饰了方法,也是错误的
参考:
如何正确使用const,static,extern
OC中 static 与 const 的做用
优势:
一、简洁的语法
二、更强的类型安全
三、函数式编程的支持
Swift 语言自己提供了对函数式编程的支持。
Objc 自己是不支持的,可是能够经过引入 ReactiveCocoa 这个库来支持函数式编程。
四、编写 OS X 下的自动化脚本
缺点
一、App体积变大
使用Swift 后, App 体积大概增长 5-8 M 左右,对体积大小敏感的慎用。
体积变大的缘由是由于 Swift 还在变化,因此 Apple 没有在 iOS 系统里放入 Swift 的运行库,反而是每一个 App 里都要包含其对应的 Swift 运行库。
二、Xcode 支持不够好
若是你是使用 Xcode常常卡住或者崩溃想必你是确定碰到过了,这个是目前使用 Swift 最让人头疼的事情,即便是到如今XCode 9, 有时候也会遇到这种问题,因此要看你的承受力了……
三、第三方库的支持不够多
目前确实 Swift 编写的第三方库确实很少,但能够经过桥接的方式来使用 Objc 的三方库,基本上没有太大问题。如今已经改善不少了…
四、语言版本更新带来的编译问题
语言自己还在发展,因此每次版本更新后都会出现编译不过的状况(至少到目前为止仍是),可是自从 4.0 版本发布后,改动没有 beta 时候那么大了,并且根据 Xcode 提示基本就能够解决语法变更致使的编译问题了。
使用CA作过menu菜单的展开收起(太逊了)
是否使用过CoreText或者CoreImage等?若是使用过,请谈谈你使用CoreText或者CoreImage的体验。
答:CoreText能够解决复杂文字内容排版问题。CoreImage能够处理图片,为其添加各类效果。体验是很强大,挺复杂的。
自动释放池是什么,如何工做
答:当您向一个对象发送一个autorelease消息时,Cocoa就会将该对象的一个引用放入到最新的自动释放.它仍然是个OC的对象,所以自动释放池定义的做用域内的其它对象能够向它发送消息。当程序执行到做用域结束的位置时,自动释放池就会被释放,池中的全部对象也就被释放。
NSNotification和KVO的区别和用法是什么?何时应该使用通知,何时应该使用KVO,它们的实现上有什么区别吗?若是用protocol和delegate(或者delegate的Array)来实现相似的功能可能吗?若是可能,会有什么潜在的问题?若是不能,为何?(虽然protocol和delegate这种东西面试已经面烂了…)
答:NSNotification是通知模式在iOS的实现,KVO的全称是键值观察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一个经过属性名访问属性变量的机制。例如将Module层的变化,通知到多个Controller对象时,可使用NSNotification;若是是只须要观察某个对象的某个属性,可使用KVO。 对于委托模式,在设计模式中是对象适配器模式,其是delegate是指向某个对象的,这是一对一的关系,而在通知模式中,每每是一对多的关系。委托模式,从技术上能够如今改变delegate指向的对象,但不建议这样作,会让人迷惑,若是一个delegate对象不断改变,指向不一样的对象。
你用过NSOperationQueue么?若是用过或者了解的话,你为何要使用NSOperationQueue,实现了什么?请描述它和G.C.D的区别和相似的地方(提示:能够从二者的实现机制和适用范围来描述)。
答:使用NSOperationQueue用来管理子类化的NSOperation对象,控制其线程并发数目。GCD和NSOperation均可以实现对线程的管理,区别是 NSOperation和NSOperationQueue是多线程的面向对象抽象。项目中使用NSOperation的优势是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具备面向对象的优势(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。 项目中使用GCD的优势是GCD自己很是简单、易用,对于不复杂的多线程操做,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
既然提到G.C.D,那么问一下在使用G.C.D以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,须要注意些什么?
答:使用block是要注意,若将block作函数参数时,须要把它放到最后,GCD是Grand Central Dispatch,是一个对线程开源类库,而Block是闭包,是可以读取其余函数内部变量的函数。
对于Objective-C,你认为它最大的优势和最大的不足是什么?对于不足之处,如今有没有可用的方法绕过这些不足来实现需求。若是能够的话,你有没有考虑或者实践太重新实现OC的一些功能,若是有,具体会如何作?
答:最大的优势是它的运行时特性,不足是没有命名空间,对于命名冲突,可使用长命名法或特殊前缀解决,若是是引入的第三方库之间的命名冲突,可使用link命令及flag解决冲突。
你实现过一个框架或者库以供别人使用么?若是有,请谈一谈构建框架或者库时候的经验;若是没有,请设想和设计框架的public的API,并指出大概须要如何作、须要注意一些什么方面,来使别人容易地使用你的框架。
答:抽象和封装,方便使用。首先是对问题有充分的了解,好比构建一个文件解压压缩框架,从使用者的角度出发,只需关注发送给框架一个解压请求,框架完成复杂文件的解压操做,而且在适当的时候通知给是哦难过者,如解压完成、解压出错等。在框架内部去构建对象的关系,经过抽象让其更为健壮、便于更改。其次是API的说明文档。
2、 第三方框架
AFNetworking 底层原理分析
AFNetworking主要是对NSURLSession和NSURLConnection(iOS9.0废弃)的封装,其中主要有如下类: 1). AFHTTPRequestOperationManager:内部封装的是 NSURLConnection, 负责发送网络请求, 使用最多的一个类。(3.0废弃) 2). AFHTTPSessionManager:内部封装是 NSURLSession, 负责发送网络请求,使用最多的一个类。 3). AFNetworkReachabilityManager:实时监测网络状态的工具类。当前的网络环境发生改变以后,这个工具类就能够检测到。 4). AFSecurityPolicy:网络安全的工具类, 主要是针对 HTTPS 服务。
5). AFURLRequestSerialization:序列化工具类,基类。上传的数据转换成JSON格式 (AFJSONRequestSerializer).使用很少。 6). AFURLResponseSerialization:反序列化工具类;基类.使用比较多: 7). AFJSONResponseSerializer; JSON解析器,默认的解析器. 8). AFHTTPResponseSerializer; 万能解析器; JSON和XML以外的数据类型,直接返回二进 制数据.对服务器返回的数据不作任何处理. 9). AFXMLParserResponseSerializer; XML解析器;
描述下SDWebImage里面给UIImageView加载图片的逻辑
SDWebImage 中为 UIImageView 提供了一个分类UIImageView+WebCache.h, 这个分类中有一个最经常使用的接口sd_setImageWithURL:placeholderImage:,会在真实图片出现前会先显示占位图片,当真实图片被加载出来后再替换占位图片。
加载图片的过程大体以下: 1.首先会在 SDWebImageCache 中寻找图片是否有对应的缓存, 它会以url 做为数据的索引先在内存中寻找是否有对应的缓存 2.若是缓存未找到就会利用经过MD5处理过的key来继续在磁盘中查询对应的数据, 若是找到了, 就会把磁盘中的数据加载到内存中,并将图片显示出来 3.若是在内存和磁盘缓存中都没有找到,就会向远程服务器发送请求,开始下载图片 4.下载后的图片会加入缓存中,并写入磁盘中 5.整个获取图片的过程都是在子线程中执行,获取到图片后回到主线程将图片显示出来
SDWebImage原理: 调用类别的方法:
参考:SDWebImage内部实现过程
友盟统计接口统计的全部功能
APP启动速度,APP停留页面时间等
3、算法
1.不用中间变量,用两种方法交换A和B的值
// 1.中间变量 void swap(int a, int b) { int temp = a; a = b; b = temp; } // 2.加法 void swap(int a, int b) { a = a + b; b = a - b; a = a - b; } // 3.异或(相同为0,不一样为1. 能够理解为不进位加法) void swap(int a, int b) { a = a ^ b; b = a ^ b; a = a ^ b; } 2.求最大公约数
/** 1.直接遍历法 / int maxCommonDivisor(int a, int b) { int max = 0; for (int i = 1; i <=b; i++) { if (a % i == 0 && b % i == 0) { max = I; } } return max; } /* 2.展转相除法 */ int maxCommonDivisor(int a, int b) { int r; while(a % b > 0) { r = a % b; a = b; b = r; } return b; } // 扩展:最小公倍数 = (a * b)/最大公约数 3.模拟栈操做
/** * 栈是一种数据结构,特色:先进后出 * 练习:使用全局变量模拟栈的操做 */ #include #include #include //保护全局变量:在全局变量前加static后,这个全局变量就只能在本文件中使用 static int data[1024];//栈最多能保存1024个数据 static int count = 0;//目前已经放了多少个数(至关于栈顶位置) //数据入栈 push void push(int x){ assert(!full());//防止数组越界 data[count++] = x; } //数据出栈 pop int pop(){ assert(!empty()); return data[--count]; } //查看栈顶元素 top int top(){ assert(!empty()); return data[count-1]; } //查询栈满 full bool full() { if(count >= 1024) { return 1; } return 0; } //查询栈空 empty bool empty() { if(count <= 0) { return 1; } return 0; } int main(){ //入栈 for (int i = 1; i <= 10; i++) { push(i); } //出栈 while(!empty()){ printf("%d ", top()); //栈顶元素 pop(); //出栈 } printf("\n"); return 0; } 4.排序算法
选择排序、冒泡排序、插入排序三种排序算法能够总结为以下:
* 都将数组分为已排序部分和未排序部分。 1\. 选择排序将已排序部分定义在左端,而后选择未排序部分的最小元素和未排序部分的第一个元素交换。 2\. 冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。 3\. 插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。 * 选择排序 /** * 【选择排序】:最值出如今起始端 * * 第1趟:在n个数中找到最小(大)数与第一个数交换位置 * 第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置 * 重复这样的操做...依次与第三个、第四个...数交换位置 * 第n-1趟,最终可实现数据的升序(降序)排列。 * */ void selectSort(int *arr, int length) { for (int i = 0; i < length - 1; i++) { //趟数 for (int j = i + 1; j < length; j++) { //比较次数 if (arr[i] > arr[j]) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } } * 冒泡排序 /** * 【冒泡排序】:相邻元素两两比较,比较完一趟,最值出如今末尾 * 第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n个元素位置 * 第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第n-1个元素位置 * …… …… * 第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推动,最值最后出如今第2个元素位置 */ void bublleSort(int *arr, int length) { for(int i = 0; i < length - 1; i++) { //趟数 for(int j = 0; j < length - i - 1; j++) { //比较次数 if(arr[j] > arr[j+1]) { int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } }
参考: 8大排序算法图文讲解 经常使用算法OC实现
5.折半查找(二分查找)
/** * 折半查找:优化查找时间(不用遍历所有数据) * * 折半查找的原理: * 1> 数组必须是有序的 * 2> 必须已知min和max(知道范围) * 3> 动态计算mid的值,取出mid对应的值进行比较 * 4> 若是mid对应的值大于要查找的值,那么max要变小为mid-1 * 5> 若是mid对应的值小于要查找的值,那么min要变大为mid+1 * */ // 已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置 int findKey(int *arr, int length, int key) { int min = 0, max = length - 1, mid; while (min <= max) { mid = (min + max) / 2; //计算中间值 if (key > arr[mid]) { min = mid + 1; } else if (key < arr[mid]) { max = mid - 1; } else { return mid; } } return -1; } 4、编码格式(优化细节)
参考:请Review下面的代码,并根据iOS的编码规范作出正确的修改
1.在 Objective-C 中,enum 建议使用NS_ENUM和NS_OPTIONS宏来定义枚举类型。
//定义一个枚举(比较严密) typedef NS_ENUM(NSInteger, BRUserGender) { BRUserGenderUnknown, // 未知 BRUserGenderMale, // 男性 BRUserGenderFemale, // 女性 BRUserGenderNeuter // 无性 }; @interface BRUser : NSObject @property (nonatomic, readonly, copy) NSString *name; @property (nonatomic, readonly, assign) NSUInteger age; @property (nonatomic, readonly, assign) BRUserGender gender;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender; @end //说明: //既然该类中已经有一个“初始化方法” ,用于设置 name、age 和 gender 的初始值: 那么在设计对应 @property 时就应该尽可能使用不可变的对象:其三个属性都应该设为“只读”。用初始化方法设置好属性值以后,就不能再改变了。 //属性的参数应该按照下面的顺序排列: (原子性,读写,内存管理)
2.避免使用C语言中的基本数据类型,建议使用 Foundation 数据类型,对应关系以下:
int -> NSInteger unsigned -> NSUInteger float -> CGFloat 动画时间 -> NSTimeInterval
5、其它知识点
HomeKit,是苹果2014年发布的智能家居平台。
什么是 OpenGL、Quartz 2D?
Quatarz 2d 是Apple提供的基本图形工具库。只是适用于2D图形的绘制。 OpenGL,是一个跨平台的图形开发库。适用于2D和3D图形的绘制。
ffmpeg框架:
ffmpeg 是音视频处理工具,既有音视频编码解码功能,又能够做为播放器使用。
谈谈 UITableView 的优化
1). 正确的复用cell; 2). 设计统一规格的Cell; 3). 提早计算并缓存好高度(布局),由于heightForRowAtIndexPath:是调用最频繁的方法; 4). 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口; 5). 滑动时按需加载,这个在大量图片展现,网络加载的时候很管用! 6). 减小子视图的层级关系; 7). 尽可能使全部的视图不透明化以及作切圆操做; 8). 不要动态的add 或者 remove 子控件。最好在初始化时就添加完,而后经过hidden来控制是否显示; 9). 使用调试工具分析问题。
如何实行cell的动态的行高
若是但愿每条数据显示自身的行高,必须设置两个属性,1.预估行高,2.自定义行高。 设置预估行高 tableView.estimatedRowHeight = 200。 设置定义行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension。 若是要让自定义行高有效,必须让容器视图有一个自下而上的约束。
说说你对 block 的理解
栈上的自动复制到堆上,block 的属性修饰符是 copy,循环引用的原理和解决方案。
block的循环引用;block的代码实现;为何会形成循环引用;block是如何强引用self的; 参考:Block 代码块中用到了self引用问题,致使循环引用
什么是野指针、空指针?
野指针:不知道指向了哪里的指针叫野指针。即指针指向不肯定,指针存的地址是一个垃圾值,未初始化。 空指针:不指向任何位置的指针叫空指针。即指针没有指向,指针存的地址是一个空地址,NULL。
什么是 OOA / OOD / OOP ?
OOA(Object Oriented Analysis) --面向对象分析 OOD(Object Oriented Design) --面向对象设计 OOP(Object Oriented Programming)--面向对象编程
多线程是什么
多线程是个复杂的概念,按字面意思是同步完成多项任务,提升了资源的使用效率,从硬件、操做系统、应用软件不一样的角度去看,多线程被赋予不一样的内涵,对于硬件,如今市面上多数的CPU都是多核的,多核的CPU运算多线程更为出色;从操做系统角度,是多任务,如今用的主流操做系统都是多任务的,能够一边听歌、一边写博客;对于应用来讲,多线程可让应用有更快的回应,能够在网络下载时,同时响应用户的触摸操做。在iOS应用中,对多线程最初的理解,就是并发,它的含义是原来先作烧水,再摘菜,再炒菜的工做,会变成烧水的同时去摘菜,最后去炒菜。
iOS 中的多线程
iOS中的多线程,是Cocoa框架下的多线程,经过Cocoa的封装,可让咱们更为方便的使用线程,作过C++的同窗可能会对线程有更多的理解,好比线程的创立,信号量、共享变量有认识,Cocoa框架下会方便不少,它对线程作了封装,有些封装,可让咱们建立的对象,自己便拥有线程,也就是线程的对象化抽象,从而减小咱们的工程,提供程序的健壮性。
GCD是(Grand Central Dispatch)的缩写 ,从系统级别提供的一个易用地多线程类库,具备运行时的特色,能充分利用多核心硬件。GCD的API接口为C语言的函数,函数参数中多数有Block,关于Block的使用参看这里,为咱们提供强大的“接口”,对于GCD的使用参见本文
NSOperation与Queue
NSOperation是一个抽象类,它封装了线程的细节实现,咱们能够经过子类化该对象,加上NSQueue来同面向对象的思惟,管理多线程程序。具体可参看这里:一个基于NSOperation的多线程网络访问的项目。
NSThread
NSThread是一个控制线程执行的对象,它不如NSOperation抽象,经过它咱们能够方便的获得一个线程,并控制它。但NSThread的线程之间的并发控制,是须要咱们本身来控制的,能够经过NSCondition实现。
参看 iOS多线程编程之NSThread的使用
其余多线程
在Cocoa的框架下,通知、Timer和异步函数等都有使用多线程,(待补充).
在项目何时选择使用GCD,何时选择NSOperation?
项目中使用NSOperation的优势是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具备面向对象的优势(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优势是GCD自己很是简单、易用,对于不复杂的多线程操做,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
KVO,NSNotification,delegate及block区别
KVO就是cocoa框架实现的观察者模式,通常同KVC搭配使用,经过KVO能够监测一个值的变化,好比View的高度变化。是一对多的关系,一个值的变化会通知全部的观察者。
NSNotification是通知,也是一对多的使用场景。在某些状况下,KVO和NSNotification是同样的,都是状态变化以后告知对方。NSNotification的特色,就是须要被观察者先主动发出通知,而后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,可是其优势是监听不局限于属性的变化,还能够对多种多样的状态变化进行监听,监听范围广,使用也更灵活。
delegate 是代理,就是我不想作的事情交给别人作。好比狗须要吃饭,就经过delegate通知主人,主人就会给他作饭、盛饭、倒水,这些操做,这些狗都不须要关心,只须要调用delegate(代理人)就能够了,由其余类完成所须要的操做。因此delegate是一对一关系。
block是delegate的另外一种形式,是函数式编程的一种形式。使用场景跟delegate同样,相比delegate更灵活,并且代理的实现更直观。
KVO通常的使用场景是数据,需求是数据变化,好比股票价格变化,咱们通常使用KVO(观察者模式)。delegate通常的使用场景是行为,需求是须要别人帮我作一件事情,好比买卖股票,咱们通常使用delegate。 Notification通常是进行全局通知,好比利好消息一出,通知你们去买入。delegate是强关联,就是委托和代理双方互相知道,你委托别人买股票你就须要知道经纪人,经纪人也不要知道本身的顾客。Notification是弱关联,利好消息发出,你不须要知道是谁发的也能够作出相应的反应,同理发消息的人也不须要知道接收的人也能够正常发出消息。
将一个函数在主线程执行的4种方法
GCD方法,经过向主线程队列发送一个block块,使block里的方法能够在主线程中执行。 dispatch_async(dispatch_get_main_queue(), ^{ //须要执行的方法 });
NSOperation 方法 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //主队列 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ //须要执行的方法 }]; [mainQueue addOperation:operation];
NSThread 方法
[self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];
[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];
[[NSThread mainThread] performSelector:@selector(method) withObject:nil];
[[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil]; 如何让计时器调用一个类方法
计时器只能调用实例方法,可是能够在这个实例方法里面调用静态方法。
使用计时器须要注意,计时器必定要加入RunLoop中,而且选好model才能运行。scheduledTimerWithTimeInterval方法建立一个计时器并加入到RunLoop中因此能够直接使用。
若是计时器的repeats选择YES说明这个计时器会重复执行,必定要在合适的时机调用计时器的invalid。不能在dealloc中调用,由于一旦设置为repeats 为yes,计时器会强持有self,致使dealloc永远不会被调用,这个类就永远没法被释放。好比能够在viewDidDisappear中调用,这样当类须要被回收的时候就能够正常进入dealloc中了。
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
-(void)timerMethod { //调用类方法 [[self class] staticMethod]; }
-(void)invalid { [timer invalid]; timer = nil; } 如何重写类方法
一、在子类中实现一个同基类名字同样的静态方法
二、在调用的时候不要使用类名调用,而是使用[self class]的方式调用。原理,用类名调用是早绑定,在编译期绑定,用[self class]是晚绑定,在运行时决定调用哪一个方法。
NSTimer建立后,会在哪一个线程运行
用scheduledTimerWithTimeInterval建立的,在哪一个线程建立就会被加入哪一个线程的RunLoop中就运行在哪一个线程
本身建立的Timer,加入到哪一个线程的RunLoop中就运行在哪一个线程。
id和NSObject*的区别
id是一个 objc_object 结构体指针,定义是
typedef struct objc_object *id id能够理解为指向对象的指针。全部oc的对象 id均可以指向,编译器不会作类型检查,id调用任何存在的方法都不会在编译阶段报错,固然若是这个id指向的对象没有这个方法,该崩溃仍是会崩溃的。
NSObject *指向的必须是NSObject的子类,调用的也只能是NSObjec里面的方法不然就要作强制类型转换。
不是全部的OC对象都是NSObject的子类,还有一些继承自NSProxy。NSObject *可指向的类型是id的子集。
static关键字的做用
回答一:
1.在函数体内定义的static他的做用域为该函数体,该变量在内存中只被分配一次,所以,其值在下次调用时仍维持上次的值不变; 2.在模块内的static全局变量能够被模块内全部函数访问,可是不能被模块外的其余函数访问; 3.在模块内的staic全局变量能够被这一模块内的其余函数调用,这个函数的使用范围被限制在这个模块内; 4.在类中的static成员变量属于整个类所拥有,对类的全部对象只有一份拷贝,也就是说只要是该类的对象,那么该对象的中被static修饰的成员变量都指向同一块地址。
回答二:
修饰局部变量: 1.延长局部变量的生命周期,程序结束才会销毁。 2.局部变量只会生成一分内存,只会初始化一次。 3.改变局部变量的做用域。
修饰全局变量: 1.只能在本文件中访问,修改全局变量的做用域,生命周期不会改 2.避免重复定义全局变量
在OC中static关键字使用误区
1.使用static修饰实例变量是不被容许的 2.使用static修饰了方法,也是错误的
参考: 如何正确使用const,static,extern OC中 static 与 const 的做用
使用 Swift 语言编程的优缺点 总的来讲,我认为使用 Swift 来做为编程语言的优势仍是要远远大于缺点的,并且不少缺点苹果也在逐渐改善。
优势:
一、简洁的语法 二、更强的类型安全 三、函数式编程的支持 Swift 语言自己提供了对函数式编程的支持。 Objc 自己是不支持的,可是能够经过引入 ReactiveCocoa 这个库来支持函数式编程。 四、编写 OS X 下的自动化脚本
缺点
一、App体积变大 使用Swift 后, App 体积大概增长 5-8 M 左右,对体积大小敏感的慎用。 体积变大的缘由是由于 Swift 还在变化,因此 Apple 没有在 iOS 系统里放入 Swift 的运行库,反而是每一个 App 里都要包含其对应的 Swift 运行库。 二、Xcode 支持不够好 若是你是使用 Xcode常常卡住或者崩溃想必你是确定碰到过了,这个是目前使用 Swift 最让人头疼的事情,即便是到如今XCode 9, 有时候也会遇到这种问题,因此要看你的承受力了…… 三、第三方库的支持不够多 目前确实 Swift 编写的第三方库确实很少,但能够经过桥接的方式来使用 Objc 的三方库,基本上没有太大问题。如今已经改善不少了… 四、语言版本更新带来的编译问题 语言自己还在发展,因此每次版本更新后都会出现编译不过的状况(至少到目前为止仍是),可是自从 4.0 版本发布后,改动没有 beta 时候那么大了,并且根据 Xcode 提示基本就能够解决语法变更致使的编译问题了。
本人精心整理了很全面iOS面试题资料,若是你想学想加入咱们请关注我私信“面试题”能够领取!!!记住必定要私信