iOS网络层设计git
一、网络层和业务层的对接设计github
使用哪一种交互模式来和业务层对接 : 使用Delegate为主,目的是为了(1)减小代码的分散度(2)减小业务层和网络层的耦合,网络层对于业务层应该是抽象的,隐藏了实现细节的 (3)只采用一种是限制了灵活性,方便进行维护面试
在网络层不要滥用block :(1)block会延长对象的生命期,delegate则不会
(2)block适合于在每次回调的任务都不同的状况下,若是同样则应使用delegate,苹果内部的网络层封装为delegate(离散型),AF的网络层封装为block(集约型)算法
使用一个reformer对象来封装数据转化逻辑,从而节省了业务层进行字典转模型这样相似的繁琐操做,同时为了解决直接使用字典的可读性差的问题,采用KPropertyStudentID这样的const变量来做为字典的key。数据库
使用离散型(delegate)的方式作网络层封装须要使用到继承,使用一个BaseAPIManager做为父类,来处理全部须要集约化的操做(例如一些公用信息),而后让不少子类来作离散化的操做编程
二、网络层的安全防范windows
防止竞争对手使用本身的API,为本身的API设计一个签名,服务端给出一个密钥,在每次使用API的时候进行一个hash算法的操做,将hash出来的值和服务端hash出来的值进行一个对比,若是同样,则代表是本身在使用API缓存
防止中间人攻击,使用较为安全的HTTPS协议,防止运营商在请求中加入广告安全
MVC模式和MVVM模式的区别微信
一、MVC模式存在Controller中代码臃肿的问题
之因此会出现MVC模式,是由于发如今开发中会有不少代码能够进行复用,同时事实也正是如此,MVC三个没款中,Model和View的代码确实能够由于MVC模式而进行复用,在github上也有不少开源的项目中封装了不少View,咱们能够很方便得使用这些view,model类做为一个数据转化逻辑的类也能够在同一个项目中进行屡次复用,可是Controller却很难在一个项目中进行复用,因此咱们在写代码的时候尽可能在Controller中只写那些没法复用的代码,例如将view添加到controller上,将model的数据传给view等等,可是实际上很难作到这一点,每每有不少代码咱们都不知道放在哪里,到了最后便放在了controller里面,致使controller变得十分臃肿。
二、对MVC模式中的Controller进行瘦身
咱们能够从下面几点对Controller进行瘦身:
三、MVVM模式的认知
四、总结
应该结合MVC和MVVM的各自的优势去让Controller进行瘦身,而不该该盲目地去追求新技术,亦或是过于保守,不肯意向前发展。
iOS中如何设置圆角
一、常规的设置方式带来的性能损耗
使用cornerRadius属性设置圆角是不会产生离屏渲染的,同时也不会真正在UI上产生圆角,这时候咱们须要将masksToBounds设置为YES,才可以产生在UI上的圆角效果,可是同时,这样也会致使离屏渲染。产生离屏渲染对于性能上有很大的消耗,将会下降FPS帧数,缘由是由于离屏渲染须要将图像的处理放在屏幕以外的内存缓存区进行处理,处理结束以后才把获得的图像放到主屏幕上。在这个过程当中产生最大消耗的是两次上下文的交互,将处理放到屏幕以外的缓存区,而后把获得的图像放到主屏幕上。
二、使用不产生离屏渲染的方式来创造圆角
使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; imageView.image = [UIImage imageNamed:@"1"]; //开始对imageView进行画图 UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, [UIScreen mainScreen].scale); //使用贝塞尔曲线画出一个圆形图 [[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip]; [imageView drawRect:imageView.bounds]; imageView.image = UIGraphicsGetImageFromCurrentImageContext(); //结束画图 UIGraphicsEndImageContext(); [self.view addSubview:imageView];
三、总结
微信中点击头像放大动画的思路
建立一个背景和新的UIImageView,UIImageView是位于背景之上的,先把背景的透明度改成0,而后进行动画,动画的效果是将新的UIImageView从原始的位置(这个位置是原来的UIImageView在新的背景上对应的frame)变化到放大的位置,而后监听背景的点击事件,点击的时候进行透明度和frame的相反变化便可。具体过程我封装好了上传到Github了,点击这里查看。
线上项目出现bug怎么解决
这里将会涉及到JSPatch这个框架的使用,这个框架的做用就是对bug进行热修复..后续更新
iOS开发中有哪些状况会产生循环引用
一、block
二、delegate
三、NSTimer
解决办法:使用一个NSTimer的Catagory,而后重写初始化方法,在实现中利用block,从而在调用的时候可使用weakSelf在block执行任务
autoreleasePool(加入到autoreleasePool中的对象)在何时释放?
iOS中的深浅复制
请查看这篇文章,讲得很深刻:iOS剖析深浅复制
iOS中的属性修饰符
列举的顺序就是修饰符在声明的时候的顺序
一、原子性修饰符
二、读写权限修饰符
三、内存管理修饰符
四、读写方法名修饰符
iOS中属性内存管理修饰符中的那些CP
strong vs copy
self.name = anotherName;
例如上面的代码,使用strong表示的是self.name和anotherName这两个指针同时指向了一个对象,过程是self.name指向了anotherName指向的对象,而若是使用copy的话,self.name和anotherName这两个指针同时指向了不一样的对象,过程是copy会将anotherName所指向的对象拷贝一份出来(浅拷贝),而后让self.name指向这个被拷贝出来的对象。
strong vs weak
只要存在strong类型修饰的属性(指针)指向了一个对象,那么这个对象就不会被ARC销毁,可是对于weak类型修饰的属性(指针)指向了一个对象,若是这个对象被销毁了,那么这个属性(指针)就会被自动设置为nil。能够说weak类型的指针是没有约束做用的,只是简单弱弱地表示了一下关系。
这里还须要分析在声明控件到底应该使用strong仍是weak
若是是使用storyboard:
若是是使用纯代码:
综上,都应该使用weak去声明控件,纯代码中若是使用了strong去声明控件,那么有一种状况:若是将控件remove了,那么controller中的view里面的subviews所引用的那条线将会被切断,可是strong属性(指针)所引用的这条线依然存在,因为采用的是强引用,因此控件将不会被ARC给销毁,那么就会一直占用内存,直到控制器销毁。
weak vs assign
weak只能用于对象类型,assign能够用于基本类型,weak比起assign有一点更好,若是weak修饰的属性指向的一个对象被销毁了,那么这个属性将会自动被设置为nil指针,若是assign修饰的属性指向的一个对象被销毁了,那么这个属性不会被自动设置为nil,同时他也不知道所指向的对象已经被销毁了,这样就引起了野指针。
weak vs unsafe_unretained
若是weak修饰的属性指向的一个对象被销毁了,那么这个属性将会自动被设置为nil指针,若是unsafe_unretained修饰的属性指向的一个对象被销毁了,那么这个属性不会被自动设置为nil,同时他也不知道所指向的对象已经被销毁了,这样就引起了野指针。
assign vs unsafe_unretained
assign能修饰基本类型,unsafe_unretained只能修饰对象类型
iOS中的多线程
一、pthread
基于C语言,不经常使用
二、NSThread
须要本身手管理线程的生命周期,偶尔使用,例如获取当前线程
[NSThread currentThread];
三、GCD(Grand Central Dispatch)
GCD是苹果开发出来的多核编程的解决方案,虽然是基于C语言的,可是采用了block进行封装,使用起来也很方便,同时也很重要,推荐使用GCD进行多线程编程
四、NSOperation
是苹果对于GCD的封装,效率不及GCD
iOS中的GCD
主队列:是一个特殊的串行队列,在主线程中运行,用于刷新UI,是一个串行队列
//串行队列 dispatch_queue_t queue = dispatch_get_main_queue;
自定义建立队列: 既能够建立串行队列也能够建立并行队列。
//串行队列 dispatch_queue_t queue = dispatch_queue_create("nineteen", NULL); dispatch_queue_t queue = dispatch_queue_create("nineteen", DISPATCH_QUEUE_SERIAL); //并行队列 dispatch_queue_t queue = dispatch_queue_create("nineteen", DISPATCH_QUEUE_CONCURRENT);
全局并行队列:系统提供的并行队列
//并行队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
循环执行任务:dispatch_apply相似一个for循环,并发地执行每一项。全部任务结束后,dispatch_apply才会返回,会阻塞当前线程(相似同步执行)。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); /* *count: 循环执行次数 *queue: 队列,能够是串行队列或者是并行队列(使用串行队列可能致使死锁) *block: 任务 */ dispatch_apply(count, queue, ^(size_t i) { NSLog(@"%zu %@", i, [NSThread currentThread]); });
队列组:队列组将不少队列添加到一个组里,当组里全部任务都执行完后,它会经过一个方法通知咱们。基本流程是首先建立一个队列组,而后把任务添加到组中,最后等待队列组的执行结果。
//建立队列组 dispatch_group_t group = dispatch_group_create(); //建立队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //并行队列执行3次循环 (队列组只能用异步方法执行) dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i < 3; i++) { NSLog(@"group-01 - %@", [NSThread currentThread]); } }); //主队列执行5次循环 dispatch_group_async(group, dispatch_get_main_queue(), ^{ for (NSInteger i = 0; i < 5; i++) { NSLog(@"group-02 - %@", [NSThread currentThread]); } }); //都完成后会自动通知 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"完成 - %@", [NSThread currentThread]); });
实现单例模式
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //dispatch_once中的代码只执行一次,经常使用来实现单例 });
GCD延迟操做
//建立队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //设置延时,单位秒 double delay = 3; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{ //3秒后须要执行的任务 });
GCD中的死锁场景
五个案例了解GCD的死锁
一、
案例:
NSLog(@"1"); // 任务1 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); // 任务2 }); NSLog(@"3"); // 任务3
结果:
1
二、
案例:
NSLog(@"1"); // 任务1 dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ NSLog(@"2"); // 任务2 }); NSLog(@"3"); // 任务3
结果:
1 2 3
三、
案例:
dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"1"); // 任务1 dispatch_async(queue, ^{ NSLog(@"2"); // 任务2 dispatch_sync(queue, ^{ NSLog(@"3"); // 任务3 }); NSLog(@"4"); // 任务4 }); NSLog(@"5"); // 任务5
结果:
1 5 2 // 5和2的顺序不必定
四、
案例:
NSLog(@"1"); // 任务1 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); // 任务2 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"3"); // 任务3 }); NSLog(@"4"); // 任务4 }); NSLog(@"5"); // 任务5
结果:
1 2 5 3 4 // 5和2的顺序不必定
五、
案例:
dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"1"); // 任务1 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); // 任务2 }); NSLog(@"3"); // 任务3 }); NSLog(@"4"); // 任务4 while (1) { } NSLog(@"5"); // 任务5
结果:
1 4 // 1和4的顺序不必定
iOS中的递归锁
若是加锁操做处于一个循环或者递归中,在第一次加锁尚未解锁的时候,就进行了第二次加锁,因此就形成死锁现象,这时候应该使用递归锁来防止死锁的发生。
iOS中的ARC是怎么解决内存管理问题的
ARC会自动处理对象的声明周期,编译的时候在合适的地方插入内存管理代码
ARC中autorelease的使用场景
iOS中的RunLoop
通常主线程会自动运行RunLoop,咱们通常状况下不会去管。在其余子线程中,若是须要咱们须要去管理。使用RunLoop后,能够把线程想象成进入了一个循环;若是没有这个循环,子线程完成任务后,这个线程就结束了。因此若是须要一个线程处理各类事件而不让它结束,就须要运行RunLoop。
SDWebImage是怎么使用RunLoop的
- (void)start{ ... self.connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO]; ... if(self.connection){ ... CFRunLoopRun( ) ... } }
- (void)cancelInternalAndStop { if (self.isFinished) return; [self cancelInternal]; CFRunLoopStop(CFRunLoopGetCurrent()); }
在建立self.connection成功后,执行了CFRunLoopRun(),开启了runloop。在failed或finished的时候会调用CFRunLoopStop中止runloop。若是不开启runloop的话,在执行完start ()后任务就完成了,NSURLConnection的代理就不会执行了。runloop至关于子线程的循环,能够灵活控制子线程的生命周期。
AFNetworking是怎么使用RunLoop的
AFNetworking解决这个问题采用了另外一种方法:单独起一个global thread,内置一个runloop,全部的connection都由这个runloop发起,回调也都由它接收。这是个不错的想法,既不占用主线程,又不耗CPU资源:
iOS中的响应链
NSRunloop、runloop、autoreleasePool、thread
NSRunloop:NSRunloop是一个消息循环,它会检测输入元和定时源,而后作回调处理。NSRunloop封装了windows中的消息处理,将SendMessage、PostMessage、GetMessage等细节封装了起来。关于NSRunloop须要着重了解这几点内容:
runloop:新建iOS项目的时候会看到在main方法中会手动建立一个autoreleasePool,程序开始时建立,结束时销毁,若是只是从表面上来看的话,那么这样和内存泄露是没有什么区别的。其实,对于每个runloop,系统会隐式地建立一个autoreleasePool,这样全部的autoreleasePool构成一个栈式的结构,在每个runloop结束的时候,当前栈顶的autoreleasePool就会被弹出,同时销毁,其中的全部对象也一样被销毁。这里所指的runloop不是NSRunloop,这里的runloop多是一个UI事件,一个timer等等,具体来讲指的是从接受到消息,处处理完这个消息的一个完整过程。
autoreleasePool和thread:
thread是不会自动建立autoreleasePool的
drawRect的做用
layoutSubviews的做用
自定义控件
数据持久化的几种方式的对比
Plist文件(属性列表):
plist文件是将某些特定的类,经过XML文件的方式保存在目录中,这些类包括(若是存在对应的可变类也包括可变类)
NSArray
NSDictionary
NSData
NSString
NSNumber
NSDate
Preference(偏好设置):
NSKeyedArchiver(归档):
归档在iOS中是另外一种形式的序列化,只要遵循了NSCoding协议的对象均可以经过它实现序列化
app的状态
UIView和CALayer的区别
KVO的实现原理