iOS开发 主要概念总结

361 阅读17分钟
一,用什么架构、有什么区别?(MVC、MVVM)

1,MVC,即Model-View-Controller。
它是苹果公司官方推荐的App开发架构,也是一般开发者最先遇到,最经典的架构。
它把整个App分为三个部分:Model负责处理数据;View负责处理UI;Controller是View和Model的桥梁,它将数据从Model层传送到View层并展示出来,同时将View层的交互传到Model层以改变数据。
它是应用的一种基本架构,主要目的是将不同的代码归并为不同的模块,做到低耦合、代码分配合理、易于扩展维护。

2,MVVM,MVVM衍生于MVC,是对 MVC 的一种演进,它促进了 UI 代码与业务逻辑的分离。
在MVVM 中,view 和 view controller正式联系在一起,我们把它们视为一个组件。
view 和 view controller 都不能直接引用model,而是引用视图模型(viewModel)。
viewModel 是一个放置用户输入验证逻辑,视图显示逻辑,发起网络请求和其他代码的地方。
使用MVVM会轻微的增加代码量,但总体上减少了代码的复杂性。
3,MVVM是MVC的升级版,完全兼容当前的MVC架构,MVVM虽然促进了UI 代码与业务逻辑的分离,一定程度上减轻了ViewController的臃肿度,但是View和ViewModel之间的数据绑定使得 MVVM变得复杂和难用了,如果我们不能更好的驾驭两者之间的数据绑定,同样会造成Controller 代码过于复杂,代码逻辑不易维护的问题。

二,解释一下多线程

1,OC中多线程根据封装程度可以分为三个层次:NSThread、GCD和NSOperation。

2,NSThread,NSThread是封装程度最小最轻量级的,使用更灵活,但要手动管理线程的生命周期、线程同步和线程加锁等,开销较大。

3,GCD(Grand Central Dispatch),又叫大中央调度,是 Apple 开发的一个多核编程的较新的解决方法。
它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。提供了简洁的C语言接口,使用更加简单高效,也是苹果推荐的方式。
可以自动管理线程的生命周期(创建线程、调度任务、销毁线程)。
可用于多核的并行运算。
会自动利用更多的 CPU 内核(比如双核、四核)
只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码。
主要是一些同步执行、异步执行、串行队列、并行队列。
将"任务"(block)添加到 队列(串行/并发/主队列/全局队列]),并且指定执行任务的函数(同步/异步)。
一次执行。
延迟执行。

4,NSOperation,NSOperation是一个抽象类,不能直接使用,其目的就是为了定义子类共有的方法和属性。
NSOperation、NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。
其子类有两个:NSInvocationOperation、NSBlockOperation。
本质上就是GCD的并发队列,就是异步执行任务。

三,RunTime

1,Runtime是属于Object-C的底层,是一套比较底层的纯C语言API,属于C语言库。

2,在我们平时编写的Object-C代码中, 程序运行过程时, 其实最终都是转成了Runtime的C语言代码, Runtime算是Object-C的幕后工作者。

3,静态语言:如C语言,编译阶段就要决定调用哪个函数,如果函数未实现就会编译报错。
动态语言:如OC语言,编译阶段并不能决定真正调用哪个函数,只要函数声明过即使没有实现也不会报错。
OC是一门动态语言,就是因为它总是把一些决定性的工作从编译阶段推迟到运行时阶段。
OC代码的运行不仅需要编译器,还需要运行时系统(Runtime Sytem)来执行编译后的代码。

4,Runtime 实际上是一个库,这个库使我们可以在程序运行时动态的创建对象、检查对象,修改类和对象的方法。

5,消息(方法)传递、消息转发、

6,关联对象(Objective-C Associated Objects)给分类增加属性。

7,魔法(Method Swizzling)方法添加和替换。
在 Objective-C 的运行时中,每个类有两个方法都会自动调用。+load 是在一个类被初始装载时调用,+initialize 是在应用第一次调用该类的类方法或实例方法前调用的。两个方法都是可选的,并且只有在方法被实现的情况下才会被调用。

@implementation ViewController

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        SEL originalSelector = @selector(viewDidLoad);
        SEL swizzledSelector = @selector(jkviewDidLoad);
        
        Method originalMethod = class_getInstanceMethod(class,originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
        
        //judge the method named  swizzledMethod is already existed.
        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        // if swizzledMethod is already existed.
        if (didAddMethod) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }
        else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

- (void)jkviewDidLoad {
    NSLog(@"替换的方法");
    
    [self jkviewDidLoad];
}

- (void)viewDidLoad {
    NSLog(@"自带的方法");
    
    [super viewDidLoad];
}

@end

8,KVO的实现。
全称是Key-value observing,翻译成键值观察。提供了一种当其它对象属性被修改的时候能通知当前对象的机制。

9,实现字典和模型的自动转换(MJExtension)。
原理描述:用runtime提供的函数遍历Model自身所有属性,如果属性在json中有对应的值,则将其赋值。
核心方法:在NSObject的分类中添加方法

10,实现NSCoding的自动归档和自动解档。
原理描述:用runtime提供的函数遍历Model自身所有属性,并对属性进行encode和decode操作。
核心方法:在Model的基类中重写方法:

四,RunLoop

1,RunLoop,是多线程的法宝,即一个线程一次只能执行一个任务,执行完任务后就会退出线程。主线程执行完即时任务时会继续等待接收事件而不退出。非主线程通常来说就是为了执行某一任务的,执行完毕就需要归还资源,因此默认是不运行RunLoop的;

2,每一个线程都有其对应的RunLoop,只是默认只有主线程的RunLoop是启动的,其它子线程的RunLoop默认是不启动的,若要启动则需要手动启动;

3,在一个单独的线程中,如果需要在处理完某个任务后不退出,继续等待接收事件,则需要启用RunLoop;

4,NSRunLoop提供了一个添加NSTimer的方法,可以指定Mode,如果要让任何情况下都回调,则需要设置Mode为Common模式;

5,保证线程的长时间存活;

6,保证NSTimer在视图滑动时,依然能正常运转;

五,常用的设计模式

1,单例模式(Singleton):
此模式保证对于一个特有的类,只有一个公共的实例存在。它一般与懒加载一起出现,只有被需要时才会创建。单例模式的例子有UserDefaults、UIApplication、UIScreen;

2,观察者模式(Observer):
它定义对象之间的一种一对多的依赖关系,每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。在iOS中的典型实现是NotificationCenter和KVO;
KVO,Key-Value Observer,即键值观察。它是一种没有通知中心的观察者模式的实现方式。一个主体对象管理所有依赖于它的观察者对象,并且在自身状态发生改变时主动通知观察者对象。
它的具体实现步骤如下。
(1)注册观察者
(2)更改主题对象属性的值,即触发发送更改的通知。
(3)在制定的回调函数中,处理收到的更改通知。

六,项目的优化

快:使用时避免出现卡顿,响应速度快,减少用户等待的时间,满足用户期望。
稳:不要在用户使用过程中崩溃和无响应。
省:节省流量和耗电,减少用户使用成本,避免使用时导致手机发烫。
小:安装包小可以降低用户的安装成本。

1,重用问题:如UITableViewCells、UICollectionViewCells、UITableViewHeaderFooterViews设置正确的reuseIdentifier,充分重用;
尽量把views设置为不透明:当opque为NO的时候,图层的半透明取决于图片和其本身合成的图层为结果,可提高性能;

2,不要使用太复杂的XIB/Storyboard:载入时就会将XIB/storyboard需要的所有资源,包括图片全部载入内存,即使未来很久才会使用。那些相比纯代码写的延迟加载,性能及内存就差了很多;

3,选择正确的数据结构:学会选择对业务场景最合适的数组结构是写出高效代码的基础。比如,数组: 有序的一组值。使用索引来查询很快,使用值查询很慢,插入/删除很慢。字典: 存储键值对,用键来查找比较快。集合: 无序的一组值,用值来查找很快,插入/删除很快。

4,延迟加载:对于不应该使用的数据,使用延迟加载方式。对于不需要马上显示的视图,使用延迟加载方式。比如,网络请求失败时显示的提示界面,可能一直都不会使用到,因此应该使用延迟加载。

5,数据缓存:对于cell的行高要缓存起来,使得reload数据时,效率也极高。而对于那些网络数据,不需要每次都请求的,应该缓存起来,可以写入数据库,也可以通过plist文件存储,例如个人信息的一些数据,也许会有好几个页面会用到,那么就把数据存入本地,使用的时候根据需要可以直接从本地进行加载。

6,不要阻塞主线程,主线程主要做以下几个方面工作:
UI 生命周期控制、系统事件处理、消息处理、界面布局、界面绘制、界面刷新,除此之外,应该尽量避免将其他处理放在主线程中,特别复杂的数据计算和网络请求等。

7,处理内存警告
在app delegate中使用applicationDidReceiveMemoryWarning的方法;
注册并接收 UIApplicationDidReceiveMemoryWarningNotification的通知;

8,选择正确的数据存储选项

9,重用和延迟加载(lazy load) Views
更多的view意味着更多的渲染,也就是更多的CPU和内存消耗。
不要一次创建所有的subview,而是当需要时才创建。

10,尽量把views设置为透明
如果你有透明的Views你应该设置它们的opaque属性为YES。
原因是这会使系统用一个最优的方式渲染这些views。
在相对比较静止的画面中,设置这个属性不会有太大影响。然而当这个view嵌在scroll view里边,或者是一个复杂动画的一部分,不设置这个属性的话会在很大程度上影响app的性能。

七,KVC、KVO

1,键值编码是一种间接访问对象的属性使用字符串来标识属性,而不是通过调用存取方法,直接或通过实例变量访问的机制,非对象类型的变量将被自动封装或者解封成对象,很多情况下会简化程序代码;

2,KVC的缺点:一旦使用 KVC 你的编译器无法检查出错误,即不会对设置的键、键路径进行错误检查,且执行效率要低于合成存取器方法和自定的 setter 和 getter 方法。因为使用 KVC 键值编码,它必须先解析字符串,然后在设置或者访问对象的实例变量。

3,键值观察机制是一种能使得对象获取到其他对象属性变化的通知 ,极大的简化了代码。

4,实现 KVO 键值观察模式,被观察的对象必须使用 KVC 键值编码来修 改它的实例变量,这样才能被观察者观察到。因此,KVC是KVO的基础。

5,注册观察者(注意:观察者和被观察者不会被保留也不会被释放)

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath 
options:(NSKeyValueObservingOptions)options 
context:(void *)context; 

6,接收变更通知

- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

7,移除对象的观察者身份

- (void)removeObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath;

8,KVO中谁要监听谁注册,然后对响应进行处理,使得观察者与被观察者完全解耦。KVO只检测类中的属性,并且属性名都是通过NSString来查找,编译器不会检错和补全,全部取决于自己。

八,Block、通知、代理、

1,代理是一种回调机制,且是一对一的关系,Delegate需要定义协议方法,代理对象实现协议方法,并且需要建立代理关系才可以实现通信;

2,通知是一对多的关系,一个对向所有的观察者提供变更通知;

3,效率:Delegate比NSNOtification高;

4,Block一般是一对一的通信;

5,Block:Block更加简洁,不需要定义繁琐的协议方法,但通信事件比较多的话,建议使用Delegate;

九,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解析器;

十,Scoket连接和HTTP连接的区别

1,HTTP连接:短连接,客户端向服务器发送一次请求,服务器响应后连接断开,节省资源。服务器不能主动给客户端响应(除非采用HTTP长连接技术),iPhone主要使用类NSURLConnection。

2,Socket连接:长连接,客户端跟服务器端直接使用Socket进行连接,没有规定连接后断开,因此客户端和服务器段保持连接通道,双方可以主动发送数据,一般多用于游戏Socket默认连接超时时间是30秒,默认大小是8K(理解为一个数据包大小)。

十一,isa指针

1,在 ios 开发中,类也是一个对象,我们称之为类对象, 所有对象中,包含实例对象和类对象,都含有一个isa 指针。实例对象的isa指针, 指向他的类对象,类对象的isa 指针, 指向他的元类。

2,系统判断一个对象属于哪个类,也是通过这个对象的isa指针的指向来判断。

3,对象中的成员变量,存储在对象本身,对象的实例方法,存储在他的isa 指针所指向的对象中,即:减号方法存储在类对象中,类方法存储在类对象isa所指向的元类中,成员变量的值储存在对象中。

4,对象在调用减号方法的时候,系统会在对象的isa指针所指向的类对象中寻找方法,这一段在kvo的实现原理中就能看到,kvo的实现原理就是系统动态的生成一个类对象,这个类是监听对象的类的子类,在生成的类中重写了监听属性的set方法,实现对set方法的监听,之后将监听对象的isa指针指向系统动态生成的这个类,当监听对象调用set方法时,由于监听对象的isa指针指向的是刚刚动态生成的类,所以在其中找的的set方法也是重写过有监听功能的set方法,这就是kvo的实现原理。同理,我们也可以通过rutime中的方法设置某个对象isa指针指向的类对象,让对象调用一些原本不属于他的方法。

十二,H5、CSS、JavaScript

十三,WebView,JS和原生的交互

iOS8.0之后,苹果推荐使用WebKit框架中的WKWebView来加载网页,使用WKWebViewConfiguration来配置JS交互。
网页切换

[_webView goBack];    //页面后退

[_webView goForward];    //页面前进

[_webView reload];    //刷新当前页面

js调用OC
主要依靠WKScriptMessageHandler协议类、WKUserContentController其中:
WKUserContentController对象负责注册JS方法,设置处理接收JS方法的代理,代理遵守WKScriptMessageHandler,实现捕捉到JS消息的回调方法。
1、配置与JS的交互
用WKWebViewConfiguration来配置JS交互

2、使用WKUserContentController,用来做 原生与JavaScript的交互管理

3、使用协议类WKScriptMessageHandler,用来处理监听JavaScript方法从而调用原生OC方法。(和WKUserContentController搭配使用)

4、通过 接收JS传出消息的name 进行捕捉的回调方法
ps:遵守WKScriptMessageHandler协议,代理是由WKUserContentControl设置

OC调用js
使用WKUserScript,执行自定义的JavaScript代码

WKWebView涉及的代理方法

1、WKNavigationDelegate协议
主要处理一些跳转、加载处理操作

十四,iOS事件响应机制

iOS的事件响应必须是UIResponder对象及其子类,我们Command查看层级关系不难发现,UITextView,UILabel,UIButton等控件他们都是UIResponder的子类。这也是他们能够响应事件的基础。

当触摸事件发生时,首先系统通过 hitTest方法找到能最合适的view,所谓最合适的view 其实就是 触摸事件 他的触摸点是否在这个View上,一个点可能在多个叠加的视图上,所以系统会找到所有的view(点所在的view),方向是从底下往上,所以判断点的顺序就是父view->子view->子view的子view,这样遍历下去,配合pointconvert方法和pointinside方法去判断这个点是否在这个view上,在就遍历他的子view,直到不满足条件。

事件的传递

系统把事件加入到一个由UIApplication管理的事件队列中
之所以加入队列而不是栈是因为队列先进先出,意味着先产生的事件,先处理

然后,事件会按照UIApplication -> UIWindow -> SuperView -> SubView的顺序不断的检测

而检测就是靠两个方法hitTest与pointInside

十五,iOS内存管理

1,首先,严格的内存管理,能够是我们的应用程在性能上有很大的提高,如果忽略内存管理,可能导致应用占用内存过高,导致程序崩溃。

2,内存管理方式主要分为3种,ARC(自动内存计数)、MRC(手动内存计数)、内存池

3,自动内存计数和手动内存计数都是用计数管理。
每个对象都有一个引用计数器,每个新对象的计数器是1,当对象的计数器减为0时,就会被销毁;
通过retain可以让对象的计数器+1、release可以让对象的计数器-1;
保证任何时候指向对象的指针个数和对象的引用计数相同,多一个指针指向这个对象这个对象的引用计数就加1,少一个指针指向这个对象这个对象的引用计数就减1。没有指针指向这个对象对象就被释放了。

4,内存管理遵循谁使用、谁创建;谁引用、谁管理的原则。

5,被存入到自动释放池内的对象,当自动释放池被销毁时,会对池内的对象全部做一次release操作。

2.7 自动释放池,什么时候创建?

程序刚启动的时候,也会创建一个自动释放池

产生事件以后,运行循环开始处理事件,就会创建自动释放池

2.8 什么时候销毁的?

程序运行结束之前销毁

事件处理结束以后,会销毁自动释放池

还有在池子满的时候,也会销毁