标签:val pos number 优点 变量 uiview 修饰符 archive block
iOS网络层设计
1、网络层和业务层的对接设计
使用哪种交互模式来和业务层对接 : 使用Delegate为主,目的是为了(1)减少代码的分散度(2)减少业务层和网络层的耦合,网络层对于业务层应该是抽象的,隐藏了实现细节的 (3)只采用一种是限制了灵活性,方便进行维护
在网络层不要滥用block :(1)block会延长对象的生命期,delegate则不会
(2)block适合于在每次回调的任务都不一样的情况下,如果一样则应使用delegate,苹果内部的网络层封装为delegate(离散型),AF的网络层封装为block(集约型)
使用一个reformer对象来封装数据转化逻辑,从而节省了业务层进行字典转模型这样类似的繁琐操作,同时为了解决直接使用字典的可读性差的问题,采用KPropertyStudentID这样的const变量来作为字典的key。
使用离散型(delegate)的方式做网络层封装需要使用到继承,使用一个BaseAPIManager作为父类,来处理所有需要集约化的操作(例如一些公用信息),然后让很多子类来做离散化的操作
2、网络层的安全防范
防止竞争对手使用自己的API,为自己的API设计一个签名,服务端给出一个密钥,在每次使用API的时候进行一个hash算法的操作,将hash出来的值和服务端hash出来的值进行一个对比,如果一样,则表明是自己在使用API
防止中间人攻击,使用较为安全的HTTPS协议,防止运营商在请求中加入广告
MVC模式和MVVM模式的区别
1、MVC模式存在Controller中代码臃肿的问题
之所以会出现MVC模式,是因为发现在开发中会有很多代码可以进行复用,同时事实也正是如此,MVC三个没款中,Model和View的代码确实可以因为MVC模式而进行复用,在github上也有很多开源的项目中封装了很多View,我们可以很方便得使用这些view,model类作为一个数据转化逻辑的类也可以在同一个项目中进行多次复用,但是Controller却很难在一个项目中进行复用,所以我们在写代码的时候尽量在Controller中只写那些无法复用的代码,例如将view添加到controller上,将model的数据传给view等等,但是实际上很难做到这一点,往往有很多代码我们都不知道放在哪里,到了最后便放在了controller里面,导致controller变得十分臃肿。
2、对MVC模式中的Controller进行瘦身
我们可以从下面几点对Controller进行瘦身:
3、MVVM模式的认知
4、总结
应该结合MVC和MVVM的各自的优点去让Controller进行瘦身,而不应该盲目地去追求新技术,亦或是过于保守,不愿意向前发展。
iOS中如何设置圆角
1、常规的设置方式带来的性能损耗
使用cornerRadius属性设置圆角是不会产生离屏渲染的,同时也不会真正在UI上产生圆角,这时候我们需要将masksToBounds设置为YES,才能够产生在UI上的圆角效果,但是同时,这样也会导致离屏渲染。产生离屏渲染对于性能上有很大的消耗,将会降低FPS帧数,原因是因为离屏渲染需要将图像的处理放在屏幕之外的内存缓存区进行处理,处理结束之后才把得到的图像放到主屏幕上。在这个过程中产生最大消耗的是两次上下文的交互,将处理放到屏幕之外的缓存区,然后把得到的图像放到主屏幕上。
2、使用不产生离屏渲染的方式来创造圆角
使用贝塞尔曲线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];
3、总结
微信中点击头像放大动画的思路
创建一个背景和新的UIImageView,UIImageView是位于背景之上的,先把背景的透明度改为0,然后进行动画,动画的效果是将新的UIImageView从原始的位置(这个位置是原来的UIImageView在新的背景上对应的frame)变化到放大的位置,然后监听背景的点击事件,点击的时候进行透明度和frame的相反变化即可。具体过程我封装好了上传到Github了,点击这里查看。
线上项目出现bug怎么解决
这里将会涉及到JSPatch这个框架的使用,这个框架的作用就是对bug进行热修复..后续更新
iOS开发中有哪些情况会产生循环引用
1、block
2、delegate
3、NSTimer
解决办法:使用一个NSTimer的Catagory,然后重写初始化方法,在实现中利用block,从而在调用的时候可以使用weakSelf在block执行任务
autoreleasePool(加入到autoreleasePool中的对象)在什么时候释放?
iOS中的深浅复制
请查看这篇文章,讲得很深入:iOS剖析深浅复制
iOS中的属性修饰符
列举的顺序就是修饰符在声明的时候的顺序
1、原子性修饰符
2、读写权限修饰符
3、内存管理修饰符
4、读写方法名修饰符
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中的多线程
1、pthread
基于C语言,不常用
2、NSThread
需要自己手管理线程的生命周期,偶尔使用,例如获取当前线程
[NSThread currentThread];
3、GCD(Grand Central Dispatch)
GCD是苹果开发出来的多核编程的解决方案,虽然是基于C语言的,但是采用了block进行封装,使用起来也很方便,同时也很重要,推荐使用GCD进行多线程编程
4、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的死锁
1、
案例:
NSLog(@"1"); // 任务1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2"); // 任务2
});
NSLog(@"3"); // 任务3
结果:
1
2、
案例:
NSLog(@"1"); // 任务1
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"2"); // 任务2
});
NSLog(@"3"); // 任务3
结果:
1
2
3
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的顺序不一定
4、
案例:
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的顺序不一定
5、
案例:
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的实现原理
标签:val pos number 优点 变量 uiview 修饰符 archive block
原文地址:http://www.cnblogs.com/wanghuaijun/p/6139874.html