小集自用
1.如何实现一个倒计时功能,类似于蘑菇街中的秒杀
step1:计算当前时间与服务器返回截止时间的时间差
step2:递减时间差,这里建议用GCD来实现倒计时功能
step3:根据递减后的时间差来计算需要显示的时间格式
代码部分:
{
NSInteger _countDownSeconds;
}
- (void)viewDidLoad {
[super viewDidLoad];
_countDownSeconds = [self caculateBeforTime:[self getNowString] AfterTime:@"2019-04-03 23:00:00"];
[self countDownGCD];
}
#pragma mark - 获取当前时间字符串
- (NSString *)getNowString
{
NSDate *date = [NSDate date];
NSDateFormatter * formater=[[NSDateFormatter alloc] init];
formater.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSString *nowString = [formater stringFromDate:date];
return nowString;
}
#pragma mark - 计算两个时间字符串的间隔秒数
- (NSTimeInterval)caculateBeforTime:(NSString *)befortime AfterTime:(NSString *)afterTime
{
NSDateFormatter *formater=[[NSDateFormatter alloc] init];
formater.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDate *beforDate = [formater dateFromString:befortime];
NSDate *afterDate = [formater dateFromString:afterTime];
NSTimeInterval time = afterDate.timeIntervalSince1970 - beforDate.timeIntervalSince1970;
return time;
}
#pragma mark - GCD定时器
- (void)countDownGCD
{
__block NSInteger leftSeconds = _countDownSeconds;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行
dispatch_source_set_event_handler(_timer, ^{
if (leftSeconds <= 0) {
dispatch_source_cancel(_timer);//关闭定时器
dispatch_async(dispatch_get_main_queue(), ^{
//主线程刷新
});
}else {
NSInteger hours = (int)(leftSeconds/3600);
NSInteger minute = (int)(leftSeconds%3600)/60;
NSInteger second = (int)leftSeconds%60;
NSString *strTime = [NSString stringWithFormat:@"倒计时:%02ld : %02ld : %02ld", hours, minute, second];
NSLog(@"%@",strTime);
}
leftSeconds--;
});
dispatch_resume(_timer);
}
2.iOS屏幕尺寸了解
UI设计师给到的高清图标注给的一般是像素px(Pixel),而iOS给的尺寸单位是pt(point),1pt = 2px,除以2即可。
App icon: 40x40 60X60 58x58 87X87 80x80 120x120 180x180 1024 x1024(无圆角)
App 启动页:640 × 960(4,4s),640 × 1136(5,5s,5c,se),750 × 1334(6,6s,7,8),1242 × 2208(6+,6s+,7+,8+),1125 x 2436(x,xs), 828 x 1792(xr), 1242 x 2688(xs max)
3.进程和线程的理解
进程是资源分配的最小单位,线程是程序执行的最小单位。一个进程可以由多个线程组成。进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。例如,一个App就相当于是一个进程,App里面一个线程死掉了,整个App会crash相当于进程死掉,而这个Appcrash并不会影响其他App。
4.TCP和UDP的理解
TCP:TCP(传输控制协议)是一种面向连接、可靠的、基于字节流的传输层通信协议。数据传递之前,客户端和服务端会有三次握手来建立连接。①.客户端告诉服务器端我要准备发送数据了②.服务端收到客户端的消息告诉客户端我已经准备好了,你可以发了③.客户端收到服务端的消息,说好的,接下来我就发。
UDP:UDP(用户数据报协议)是一种无连接的传输层协议。UDP传送数据不需要和服务器连接,只需要知道ip和监听端口,不需要链接没有目的的socket,只是将数据报投递出去,不管接收方是否成功接收到,因此是一种不可靠的传输。可在无线局域网通讯中使用。
TCP | UDP | |
---|---|---|
是否连接 | 面向连接 | 面向非连接 |
可靠性 | 可靠 | 不可靠 |
传输速度 | 慢 | 快 |
应用场景 | 传输大量数据 | 传输少量数据 |
5.UIView的生命周期
init-----1
willMoveToSuperview:-----2
didMoveToSuperview-----3
willMoveToWindow:-----4
didMoveToWindow-----5
layoutSubviews-----6
willMoveToWindow:-----7
didMoveToWindow-----8
willMoveToSuperview:-----9
didMoveToSuperview-----10
removeFromSuperview-----11
dealloc-----12
代码部分:
{
NSInteger _countCycle;
}
- (instancetype)init
{
self = [super init];
if (self) {
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)didAddSubview:(UIView *)subview
{
[super didAddSubview:subview];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)willRemoveSubview:(UIView *)subview
{
[super willRemoveSubview:subview];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)willMoveToSuperview:(nullable UIView *)newSuperview
{
[super willMoveToSuperview:newSuperview];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)willMoveToWindow:(nullable UIWindow *)newWindow
{
[super willMoveToWindow:newWindow];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)removeFromSuperview
{
[super removeFromSuperview];
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
- (void)dealloc
{
_countCycle++;
NSLog(@"%@-----%ld",NSStringFromSelector(_cmd),_countCycle);
}
进入界面.png
退出界面.png
6.简述 NotificationCenter、KVC、KVO、Delegate
- Notification:controller向defaultNotificationCenter中添加一个notification,其他类注册这个notification就可以收到通知,而这些类在收到通知之后可以做一些自己的操作。(观察者模式不仅仅可以用NotificationCenter来实现,还有KVO)。多观察者默认随机顺序发通知给观察者们,只有等当前观察者完成操作时才会去执行下一个观察者需要执行的操作,可以用NSNotificationQueue( 通知队列)改变观察者响应的顺序,一对多关系。
- KVC(key-value coding):键值编码,通过字符串key来间接访问属性,而不是通过调用 getter 和 setter (存取)方法访问。
- KVO(Key-value observing):键值观察机制,观测指定对象的属性,当指定对象的属性更改之后会通知相应 的观察者,一对多关系。
- Delegate:代理,给某一个对象提供一个代理对象,由代理对象对源对象的引用,一对一关系。
7.iOS中常用设计模式
- MVC:model保存数据模型和处理数据逻辑,View负责model数据和UI控件的显示,Controller负责model和View之间的通讯。
- 单例模式:用一个静态方法返回这个类的对象。这个对象是全局唯一的,整个项目里只开辟这一块内存,通常用在登陆之后服务器返回的用户信息存储。
- 观察者模式:通过添加观察者来观察某个对象的实例变量的变化,当被观察的对象发生改变时,通知观察者去做出相应的操作。通常用在model层对Controller和View进行的通知方式,不关心谁去接受,只负责发送通知。
- 工厂模式:快速创建对象的方式,将对象的创建和属性赋值封装成类方法。通常应用在创建常用的UI控件
- 代理模式: 代理模式给某一个对象供一个代理对象,并由代理对象控制对源对象的引用。 如招人干活,干完告诉我。
- MVVM:视图(View)、视图模型(ViewModel)、模型(Model)三部分组成,MVVM 中,我们将视图处理逻辑 从 C 中剥离出来给 V,剩下的业务逻辑部分被称做 View-Model。使用 MVVM 模式的 iOS 应用的可测试性要好于 MVC,因为 ViewModel 中并不包含对 View 的更新, 相比于 MVC,减轻了 Controller 的负担,使功能划分更加合理。
8.Object-c 的类可以多重继承么?可以实现多个接口么?Category 是什 么?重写一个类的方式用继承好还是分类好?为什么?
Objective-C的类不可以多重继承,可以实现多个接口,通过实现多个接口可以完成C++的多重继承。Category是类别,重写一个类的方式一般用分类比较好,用分类去重写类的方法,仅对本类有效,不会影响到其他类和原有类的关系。
9.#import 跟#include 有什么区别,@class 呢, #import<> 跟 #import“”有什么区别?
#import
是Objective-C导入头文件的关键字
#include
是C/C++导入头文件的关键字
使用#import
头文件只会导入一次,不会重复导入
@class
告诉编译器某个类的声明,当执行时,才会去查看类的实现文件
#import<>
用来包含系统的头文件
#import""
用来包含用户头文件
10.属性 readwrite,readonly,assign,retain,copy,nonatomic 各是什么作用,在那种情况下用?
- readwrite可读可写特性,需要生成setter和getter方法
- readonly只读特性,只会生成getter方法,不会生成setter方法,不希望属性在类外被改变
- assign赋值特性,setter方法将传入的参数变量赋值给实例变量;仅设置变量时
- retain持有特性,setter方法将传入的参数先保留,然后再赋值,传入的参数retainCount会加1
- copy拷贝特性,setter方法将传入对象复制一份
- nonatomic非原子操作
11.原子(atomic)跟非原子(non-atomic)属性有什么区别?
atomic
提供多线程安全,防止在写未完成的时候被另一个线程读取,造成数据错误
nonatomic
非原子性操作,禁止多线程,不涉及线程锁的操作,执行效率会高一些。
一般情况下都会使用nonatomic来修饰
12.我们说的OC是动态运行时语言怎么理解?
主要是将数据类型的确认由编译时推迟到运行时,这个问题其实设计两个概念:多态和运行时。简单来说,运行时机制使我们直到运行的时候才去决定一个对象的类别,以及调用该类别对象的指定方法。多态,不同的对象以自己的方式去响应相同消息的能力。运行时机制是多态的基础。
13.iOS中block用什么修饰?
iOS中block用copy修饰,block在创建的时候,内存是分配到栈上的而不是在堆上。它本身的作用域是属于创建时候的作用域,一旦在创建的作用域外面调用block将导致程序崩溃。因为栈区的特点就是创建的对象随时可能被销毁,而在对block进行copy之后,block会存放到堆上。
14.在 block 内如何修改 block 外部变量?
__block int a = 60,序号1中GCD示例代码有示例。
15.MD5和Base64的区别是什么,各自场景是什么?
MD5:
信息摘要算法,可以进行加密,但是不能解密,属于单向加密,通常用于文件校验,也可用于App中密码一般用MD5加密传至服务器,跟服务器中保存的MD5加密过后的字符串比较
Base64
可以进行加密解密,常用于http加密。