线程和进程的区别:
进程是处于运行过程中的程序,是系统资源分配和调度的一个独立单位;线程是进程的一部分,它是进程中用来执行任务的单位;一个进程是由一或多个线程组成,进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行;一个程序可以看作一个进程,一个进程至少包含一个线程,一个进程中的所有线程共享当前进程所拥有的资源;
weak实现原理:Runtime维护了一个weak表,用于存储指向某个对象的所有weak指针。weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址(这个地址的值是所指对象的地址)数组。
当weak指向的对象释放之后,weak指针置为空;当__unsafe_unretained指向的对象释放之后,指针仍然存在,此时使用可能造成崩溃
假如Controller太臃肿,如何优化?
构造viewmodel viewmodel的指针指向vc(weak),vc的指针指向viewmodel(Strong)
使用分类的方法
工具类封装
网络请求封装
UI的封装
整合常量到一个头文件
消息转发机制原理:
消息转发机制基本分为三个步骤:
1、动态方法解析 在resolveInstanceMethod方法中可以通过class_addMethod补救
2、备用接受者 在forwardingTargetForSelector可以renturn一个备用的接受者
3、完整转发 forwardInvocation把这个消息转发
KVO的理解:
基于runtime实现,大概就是新建一个类(新类),新类继承于旧类;将旧类对象的isa指针指向新类;重写新类的set方法,set方法的作用是一是执行父类的set方法,一是通知观察者
NSString为什么用copy不用strong:
当要赋值的对象是NSMutableString类型时,这时如果使用strong,这只是一个简单的指针拷贝,当NSMutableString类型的数据改变时,用strong修饰的属性也会发生改变,同理用copy时会把NSMutableString类型的数据深拷贝,不会发生这种情况
为什么ios是动态语言:
1.动态类型:
即运行时再决定对象的类型(根据isa指针找到对应的类)。简单说就是id类型;像内置的明确的基本类型都属于静态类型(int、NSString等)。静态类型在编译的时候就能被识别出来。而动态类型就编译器编译的时候是不能被识别的,要等到运行时(run time),即程序运行的时候才会根据语境来识别。
2.动态绑定:
基于动态类型,在某个实例对象被确定后,其类型便被确定了。该对象对应的属性和响应的消息也被完全确定,这就是动态绑定。比如我们一般向一个NSObject对象发送-respondsToSelector:或者 -instancesRespondToSelector:等来确定对象是否可以对某个SEL做出响应,而在OC消息转发机制被触发之前,对应的类 的+resolveClassMethod:和+resolveInstanceMethod:将会被调用,在此时有机会动态地向类或者实例添加新的 法,也即类的实现是可以动态绑定的;
3.动态加载:
所谓动态加载就是我们做开发的时候icon图片的时候在Retina设备上要多添加一个张@2x的图片,当设备更换的时候,图片也会自动的替换。
*MVVM和MVC:
MVC:简单来说就是,逻辑、试图、数据进行分层,实现解耦。
MVVM:是Model-View-ViewMode模式的简称;比MVC更加释放控制器臃肿,将一部分逻辑(耗时,公共方法,网络请求等)和数据的处理等操作从控制器里面搬运到ViewModel中
MVVM的特点:
低耦合,View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
可重用性,可以把一些视图的逻辑放在ViewModel里面,让很多View重用这段视图逻辑。
独立开发,开发人员可以专注与业务逻辑和数据的开发(ViewModel)。设计人员可以专注于界面(View)的设计。
load和initialize:
load:
1.当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),每个类的load函数只会自动调用一次
2.当父类和子类以及分类都有load方法时,先调用父类的load方法时,后调用子类的load方法,然后调用分类的load方法
3.如果有多个category实现了load方法,那么这些load方法都会执行,但是顺序不一定
initialize:
1.当类初始化时调用,本类和父类的initialize都会调用,且只会调用一次;但是子类如果没有实现initialize方法,父类会调用两次
2.当类有category时,category的initialize和父类的initialize会执行,而本类的不会执行
3.当类有多个category时,只有一个category会执行,谁执行由compile sources决定
isKindOfClass,isSubclassOfClass和isMemberOfClass
[Object isKindOfClass:Class] //判断这个类是不是这个对象的直接父类或者间接父类
[Class1 isSubclassOfClass:Class2] //判断class1是不是class2的子类
[Object isMemberOfClass:Class] //判断这个类是不是这个对象的直接父类 如:
补充:附加测试(isMemberOfClass)
结果令人意外bool7为假,难道上面得出的结论是错误的么?或者textArr不是 NSArray 类型的?打印了一下textArr 结果发现结果为
textArr类型为NSArray0
原来textArr 不是NSArray类型实例,通过不同方法实例化的array都是NSArray子类的实例,NSArray是一个抽象的基类。这种模式就是了类簇模式.
iOS 获得当前屏幕中的cell以及判断 UITableViewCell 和 UICollectionViewCell 是否移出屏幕
获取当前屏幕中的cell:
利用tabview提供的属性
visibleCells,indexPathsForVisibleRows
判断cell是否移出屏幕:
CGRect cellR = [tabV rectForRowAtIndexPath:indexP];
tabV.contentOffset.y-cellR.origin.y<myCell.frame.size.height //没有移出屏幕
#import跟 #include 有什么区别,@class呢,#import<> 跟 #import””有什么区别?
1). #import是Objective-C导入头文件的关键字,#include是C/C++导入头文件的关键字,使用#import头文件会自动只导入一次,不会重复导入。
2). @class告诉编译器某个类的声明,当执行时,才去查看类的实现文件,可以解决头文件的相互包含。
3). #import<>用来包含系统的头文件,#import””用来包含用户头文件
通知,代理,delegate和Block的区别,kvo
相同点:代理和Block大多是我们都可以用来做倒序传值的。我们都得注意避免循环引用。不然我们去使用代理还是Block的时候,都需要判断它们是否实现
不同点:1.NotificationCenter 通知中心:“一对多”,在APP中,很多控制器都需要知道一个事件,应该用通知;
2.delegate 代理委托:“一对一”,对同一个协议,一个对象只能设置一个代理delegate
3.block(闭包) block和delegate一样,一般都是“一对一”之间通信交互,相比代理block有以下特点:写法更简练,不需要写protocol、函数等,block需要注意防止循环引用
KVO性能不好(底层会动态产生新的类),只能监听某个对象属性的改 变
IOS的变量修饰词@public 、@protected、@package、@private,简称 4P
@private 作用范围只能在自身类
@protected 作用范围在自身类和继承自己类。项目里面什么多不写的,默认就是此属性。
@public 作用范围最大,在任何地方。
@package 只能用于本框架,其他的都不能访问
@dynamic关键字和@synthesize关键字是用来做什么的?
注意:这两个关键字是针对@property属性的,不是针对带下划线的_成员变量的
@dynamic :修饰的属性,其getter和setter方法编译器是不会自动帮你生成。必须自己是实现的。
@synthesize:修饰的属性,其getter和setter方法编译器是会自动帮你生成,不必自己实现。且指定与属性相对应的成员变量。
如果@synthesize和@dynamic都没写,那么默认的就是@syntheszie var = _var;
例子:NSString* _name;
@property (nonatomic, copy)NSString *name;
@synthesize name ; //注意不是 @synthesize name = _name
则: _name = @"hehe";
NSLog(@"%@",self.name); //null
如何令自己所写的对象具有拷贝功能?
如果想让自己的类具备copy方法,并返回不可变类型,必须遵循nscopying协议,并且实现
- (id)copyWithZone:(NSZone *)zone
如果让自己的类具备mutableCopy方法,并且放回可变类型,必须遵守NSMutableCopying,并实现- (id)mutableCopyWithZone:(nullable NSZone *)zone
define 和 const常量以及static有什么区别?int * const 和const int*区别?
define在预处理阶段进行替换,const和static常量在编译阶段使用
宏不做类型检查,仅仅进行替换,const常量和static有数据类型,会执行类型检查
define不能调试(打断点),const常量和static可以调试
define可以定义一些简单的函数,const不可以
static作用是“改变生命周期”,编译时就为变量分配内存,直到程序退出才释放存储单元,但是没有修改变量的作用域;const阻止一个变量被改变 ,若将成员成员函数声明为const,则该函数不允许修改类的数据成员
const限制的是它右边的数据int * const p: p为常量;const int * p: int *p为常量但是p的值可以改变
static和extern的区别?
static修饰的变量本源文件可见,extern修饰的变量原来的整个工程可见
static在不同文件可以声明同名变量,extern在不同文件不能声明同名变量
extern 不能修饰局部变量
extern "c"告诉编译器使用c的规则处理函数,而不使用C++规则处理。
autorelease 对象在什么情况下会被释放?Autoreleasepool 与 Runloop 的关系?
分两种情况:手动干预释放和系统自动释放
手动干预释放就是指定autoreleasepool,当前作用域大括号结束就立即释放
系统自动去释放:不手动指定autoreleasepool,Autorelease对象会在当前的 runloop 迭代结束时释放
NSAutoreleasePool可以同时有多个,它的组织是个栈,总是存在一个栈顶pool,也就是当前pool,每创建一个pool,就往栈里压一个,改变当前pool为新建的pool,然后,每次给pool发送drain消息,就弹出栈顶的pool,改当前pool为栈里的下一个pool。
每一个线程都会维护自己的 autoreleasepool 堆栈,每个runloop迭代中都加入了自动释放池Push和Pop
Runloop 会自动帮我们创建Autoreleasepool,并进行Push、Pop 等操作来进行内存管理,
请谈一谈UIViewController的完整生命周期(非xib模式下[[ViewController alloc]init])
-[ViewController init];
-[ViewController initWithNibName:bundle:];
-[ViewController loadView];
-[ViewController viewDidLoad];
-[ViewController viewWillAppear:];
-[ViewController viewDidAppear:];
-[ViewController viewWillDisappear:];
-[ViewController viewDidDisappear:];
xib模式:当command+N创建ViewController的时候勾选了Also create XIB file ,系统就会帮我们创建与ViewController同名的XIB文件
ViewControllerWithXib *vc = [[ViewControllerWithXib alloc]initWithNibName:@"ViewControllerWithXib"bundle:nil];
调用顺序不同的是,xib模式不调用init,其余都相同
StoryBoard模式:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil];
ViewController*vc = [storyboard instantiateViewControllerWithIdentifier:@"MyViewController"];
initWithCoder:
awakeFromNib (xib文件初始化好)
loadView(这个方法结束时 控制器的View的子控件才创建)
其余相同
nib,xib,storyboard的区别:
实际xib和storyboard都打包成nib文件;storyboard包含多个nib文件
xib和storyboard的相同点:都是用来描述软件界面
都用Interface Builder工具来编辑
本质都是转换成代码去创建控件
不同点:
Xib是轻量级的,用来描述局部 UI界面;StoryBoard是重量级的,用来描述整个软件的多个界面,并且能展示多个界面之间的跳转关系
ios延时的几种方式:
1.performSelector方法 当前runloop会创建一个timer
主线程和子线程都可以执行
是一种非阻塞的执行方式,
cancelPreviousPerformRequestsWithTarget可以取消
2.定时器:NSTimer
主线程可以执行,子线程执行需要开启runloop
是一种非阻塞的执行方式,
可以通过NSTimer类的- (void)invalidate;取消执行。
3. sleep方式
此方式在主线程和子线程中均可执行。
是一种阻塞的执行方式,
建议放到子线程中,以免卡住界面
没有找到取消执行的方法。
4.GCD方式
dispatch_after
此方式在可以在参数中选择执行的线程。
是一种非阻塞的执行方式,
dispatch_block_cancel()可以取消
ios实现多继承的方式:
注意协议可以多继承
方法一:采用组合的模式来代替继承模式
@interface ClassC : NSObject {
ClassA *a;
ClassB *b;
}
-(void)methodA{
[a methodA];
}
-(void)methodB{
[b methodB];
}
方法二:给NSObject添加category
方法三:当本类初始化时利用runtime遍历另一个类的方法和属性,然后添加到本类
ios内存区域:
栈区(stack):由编译器自动分配并释放,存放局部变量函数的参数值
堆区: 由程序员分配和释放,如果程序员不释放,程序结束时,可能会由操作系统回收;存放alloc,new等关键字生成的对象,我们常说的内存管理,就是管理这部分内存
全局区(静态区)全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量存放在一块区域,未初始化的全局变量和静态变量在相邻的另一块区域,程序结束后有系统释放;
文字常量区 存放常量字符串,程序结束后由系统释放;
程序代码区 存放函数的二进制代码
category的实现原理:
Category 在编译时,新添加的方法,都被以倒序插入到原有方法列表的最前面,所以不同的Category,添加了同一个方法,执行的实际上是最后一个,而原类中,如果有相同的方法,则排在后面。并没有被覆盖;所以这也是不建议用Category复写原类方法的原因,因为不能调用到原类的方法了,而且会造成不可预估的问题。
Http 和 Https 的区别?为什么更加安全?Https 的加密过程?
HTTPS = HTTP + SSL/TLS
HTTP 和 HTTPS 的不同之处:https协议需要到CA申请证书。
http是超文本传输协议,信息是明文传输;https 则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的身份认证的网络协议,比http协议安全。
https可以防止中间人攻击
https的根本就是证书+对称加密+不对称加密;客户端先根据证书确认对方的身份,再用公匙发送给服务器对称密匙;服务器收到对称密匙后用私匙解密得到对称密匙,然后用对称密匙收发消息
公钥证书体系对加密主要做了两个方面,一个是身份验证,一个是信息加密。
如何保持线程不退出?
已知线程的生命周期与任务有关,任务执行完毕,它会自动销毁,就算你用强指针指着它也还是不行;如果想让线程不死,可以让任务不结束,我们可以仿苹果做法,让线程有任务就执行,没执行就休眠;我们知道runloop: 只要runloop 中有model, model中有timer/source/observer/ 它就不会退出,在开发中如果是为了让一个任务不结束,我们很少创建一个timer,因为一个timer就又涉及到时间,任务这样。在开发中我们一般选择source 的方式:开一个端口
//01创建子线程的runloop
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
//02给当前子线程添加端口
[currentRunloop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
//03启动runloop(子线程的runloop需手动启动)
[currentRunloop run];
不用中间变量,用两种方法交换 A 和 B 的值
A=A+B B=A-B A=A-B或者
A = A^B; B = A^B; A = A^B; //^是异或运算,他的规则是1^1=0^0=0, 1^0=0^1=1;所以a^a=0,0与一个数的异或还是他本身。
main()之前的过程有哪些?
1)dyld 开始将程序⼆二进制⽂文件初始化包括加载动态库
2)交由ImageLoader 读取 image,其中包含了了我们的类,⽅方法等各种符号 (Class、Protocol 、Selector、 IMP)
3)由于runtime 向dyld 绑定了了回调,当image加载到内存后,dyld会通知runtime进 ⾏行行处理理
4)runtime 接⼿手后调⽤用map_images做解析和处理理
5)接下来load_images 中调⽤用call_load_methods⽅方法,遍历所有加载进来的 Class,按继承层次依次调⽤用Class的+load和其他Category的+load⽅方法
6)⾄至此 所有的信息都被加载到内存中
7)最后dyld调⽤用真正的main函数
注意:dyld会缓存上⼀一次把信息加载内存的缓存,所以第⼆二次⽐比第⼀一次启动快⼀一点
Https绝对安全吗:
中间人攻击:
开发中难免遇到http抓包的需求,有时遇到https的请求,一般抓包方式就没法正确截获数据了。不过通过伪造证书,https的包也是可以抓的,参见iOS抓包利器Charles。难道https如此脆弱?既然https能轻易被抓包,还要来干啥?
先看下原理,首先charles伪装成服务端和客户端通信,并同时伪装成客户端与服务器通信,充当中间角色,从而截获数据,如图:
这种方式起效的前提是代码关闭了证书验证:AFNetWorking中的allowInvalidCertificates,所以release时记得打开证书验证,这样伪造的证书就不被接受了。
好了,看起来安全了?等等,allowInvalidCertificates只是实现了拒绝不受信任的证书,注意,重点是信任,如果证书是受到信任的呢?虽然可能性有点小,不过假设有一个攻击者手上拥有一个受信任的证书,首先iPhone信任的证书包括一些预装的证书iOS 8 中可用的受信任根证书的列表,和用户自己安装的证书。那么即使开启了证书allowInvalidCertificates,中间人攻击依然能够发生。这时候就需要开启SSL Ping Mode了
AFNetWorking里通过AFURLConnectionOperationSSLPinningMode设置
原理是把证书打包或者公钥打包在APP中,在NSURLConnectionDelegate协议中的connection:willSendRequestForAuthenticationChallenge:中检测证书是否没被篡改。
到此为止,用户的信息基本能保证安全了,但是还是不提倡对接口做签名算法等处理,加大攻击者对接口的攻击难度
越狱后:a.使用Snoop-it
b.NSURLProtocol
利用NSURLProtocol拦截所有NSURLConnection请求。如果不清楚NSURLProtocol是用来干啥的,可以先看下这里
具体原理是利用了Apple提供的NSURLProtocol规则,后面注册的NSURLProtocol会优先被询问(调用canInitWithRequest:),所以只要简单实现个NSURLProtocol子类即可
@interface HttpDumpURLProtocol:NSURLProtocol
@end
@implementation HttpDumpURLProtocol
+(BOOL)canInitWithRequest:(NSURLRequest*)request{
NSLog(@"request:%@",request.URL.absoluteString);returnNO;
}
@end
如何理解UIWindow:
所有创建的UIWindow会按照level形成一个树形结构,优先级高的优先显示在屏幕中,非makeKeyAndVisible的window默认是hidden的,设置了hidden=NO的UIWindow才会显示,使用makeKeyAndVisible会显示该window并且保存为keywindow
NSTimer使用中的坑:
坑1:创建的方式
timerWithTimeInterval开头的构造方法,我们可以创建一个定时器,但是默认没有添加到runloop中,我们需要在创建定时器后,需要手动将其添加到NSRunLoop中,否则将不会循环执行。
scheduledTimerWithTimeInterval开头的构造方法,从此构造方法创建的定时器,它会默认将其指定到一个默认的runloop中,并且timerInterval时候后,定时器会自启动。
init是默认的初始化方法,需要我们手动添加到runloop中,并且还需要手动触发fire,才能启动定时器。
NSTimer的创建和释放必须放在同一个线程中,所以我们的创建实例的时候,一定要特别留意这几个创建方式的区别,
坑2:循环引用
runloop会对NSTimer强引用,NSTimer也会对target强引用
新建SFWeakTimerTarget类弱引用目标类,在SFWeakTimerTarget中使用NSTimer,每次都通知目标类即可
坑3:NSDefaultRunLoopMode搞怪
当使用NSTimer的scheduledTimerWithTimeInterval的方法时,事实上此时的timer会被加入到当前线程的runloop中,默认为NSDefaultRunLoopMode,如果当前线程是主线程,某些事件,如UIScrollView的拖动时,会将runloop切换到NSEventTrackingRunLoopMode模式,此时加入NSRunLoopCommonModes
weak和assign的区别:
weak 只可以修饰对象,如果修饰基本数据类型,编译器会报错。weak 不会产生野指针问题。因为weak修饰的对象释放后(引用计数器值为0),指针会自动被置nil
assign 可修饰对象,和基本数据类型,当需要修饰对象类型时,MRC时代使用unsafe_unretained,当然,unsafe_unretained也可能产生野指针,所以它名字是unsafe。assign 如果修饰对象,会产生野指针问题;如果修饰基本数据类型则是安全的,修饰的对象释放后,指针不会自动被置空。
相试点:都可以修饰对象,且不会造成引用计数+1
注意:基本数据类型在栈中内存是系统自动管理;而对象在堆中由程序员管理;所以assign修饰对象会出问题
oc语言的优缺点:
1.Category是非常实用的扩展机制,可以很方便的为一个已有的类添加属性或者方法,而不需要笨拙的去继承。
2.运行时多态的概念,可以让一个类的对象动态的以其他类行为去执行(OC中多了很多运行时态的机制,其中id的特殊用途,可以通过id类型的变量,调用不同类的同名函数,即使这些类没有任何关系)。
3.ARC不用多讲了吧
4.OC可以给已类添加新的函数。
5.由于都是C衍生出的面向对象的语言 所以可以和C++混合编码。
缺点:
1.不支持多重继承 只有多级继承。
2.使用动态运行时类型,所有的方法都是函数调用,所以很多编译时优化方法都用不到。(如内联函数等),性能低劣。
3.不支持运算符重载
performselect传递多个参数:
1.用NsInvocation代替performSelector可以实现传递多个参数
2.就是将参数进行封装成一个结构体,Class,z字典或其他的集合,然后定义一个传递单参的方法也可以实现
NSMethodSignature*singture = [[selfclass]instanceMethodSignatureForSelector:@selector(test:test2:test3:)];
NSInvocation *invo = [NSInvocation invocationWithMethodSignature:singture];
[invo setTarget:self];
[invo setSelector:@selector(test:test2:test3:)];
NSString*a=@"a";
NSString*b=@"b";
NSString*c=@"c";
[invo setArgument:&a atIndex:2];
[invo setArgument:&b atIndex:3];
[invo setArgument:&c atIndex:4];
[invo retainArguments]; //retain一下防止提前释放
[invo invoke];
imageName和imageWithContentsOfFile区别:
1.imageName可以加载Assets.car中的图片,也可以加载mainBundle中图片
imageWithContentsOfFile只能加载mainBundle中图片
2.使用imageName加载到内存当中会一直存在内存当中,(图片)不会随着对象的销毁而销毁,加载进去图片后,占用的内存归系统管理,我们是无法管理的;使用imageWithContentOfFile加载的图片会随着对象的销毁而销毁
3.imageName相同的图片是不会重复加载的;imageWithContentsOfFile相同的图片会被重复加载到内存当中
4.imageName加载到内存中占据的内存较大,imageWithContentsOfFile加载到内存中占据的内存较小
如果图片较小,并且使用频繁的图片使用imageName:方法来加载;如果图片较大,并且使用较少,使用imageWithContentOfFile:来加载。
LRU算法:(Least recently used,最近最少使用)
算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”,缓存达到一定数量后会删除最少访问的元素
链表实现:
最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:
1. 新数据插入到链表头部;
2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3. 当链表满的时候,将链表尾部的数据丢弃。
字典和数组实现:
字典放Key和value;数组放key;访问时将元素放到数组index为0处,删除时删除数组末尾元素并且删除字典中的key和value
$(PROJECT_DIR) 与$(SRCROOT)的区别:
左边的animationDemo是PROJECT_DIR
右边的animationDemo是SRCROOT