五、APP生命周期
APP有5种状态,分别是:
1、Not running未运行:app没启动或被迫终止。
2、Inactive未激活:当前应用正在前台运行,但是并不接收事件(当前或许正在执行其它代码)。一般每当应用要从一个状态切换到另一个不同的状态时,中途过渡会短暂停留在此状态。唯一在此状态停留时间比较长的情况是:当用户锁屏时,或者系统提示用户去响应某些(诸如电话来电、有未读短信等)事件的时候。
3、Active激活:当前应用正在前台运行,并且接收事件。这是应用正在前台运行时所处的正常状态。
4、Backgroud后台:程序在后台而且能执行代码,大多数程序进入这个状态后会在在这个状态上停留一会。时间到之后会进入挂起状态(Suspended)。经过特殊的请求后可以长期处于Backgroud状态。
5、Suspended挂起:程序在后台不能执行代码。系统会自动把程序变成这个状态而且不会发出通知。当挂起时,程序还是停留在内存中的,当系统内存低时,系统就把挂起的程序清除掉,为前台程序提供更多的内存。
applicationDidEnterBackground:方法允许最多有5秒的时间去完成任何任务然后返回。实际中,此方法应该尽可能快的返回。如果在时间到期之后,此方法没有返回,则应用即被kill掉,并且清除所占用的内存。如果你的应用确实需要更多的时间去执行任务,可以调用beginBackgroundTaskWithExpirationHandler:方法请求后台执行时间,然后启动一个能长期执行任务的线程。无论你是否启动一个执行后台任务的线程,applicationDidEnterBackground:方法都必须在5秒后退出。
UIViewController生命周期,子视图布局在哪里布局?是在哪个方法?方法是在什么时机调用
代码调用顺序
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil
- (void)loadView;
- (void)viewDidLoad;
- (void)viewWillAppear:(BOOL)animated;
- (void)viewWillLayoutSubviews API_AVAILABLE(ios(5.0));
// Called just after the view controller's view's layoutSubviews method is invoked. Subclasses can implement as necessary. The default is a no-op.
addSubViews:会调用此方法
控制器将要布局子控件,默认不进行任何操作。你可以在LayoutSubviews之前重写此方法布局子视图。
- (void)viewDidLayoutSubviews API_AVAILABLE(ios(5.0));
- (void)viewDidAppear:(BOOL)animated;
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated;
- (void)didReceiveMemoryWarning; // Called when the parent application receives a memory warning. On iOS 6.0 it will no longer clear the view by default.
dealloc
六、讲讲runtime,有什么体现
Runtime的特性主要是消息(方法)传递,如果消息(方法)在对象中找不到,就进行转发
动态方法解析
首先,Objective-C运行时会调用 +resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数并返回YES, 那运行时系统就会重新启动一次消息发送的过程。
如果resolve方法返回 NO ,运行时就会移到下一步:forwardingTargetForSelector。
完整消息转发
如果在上一步还不能处理未知消息,则唯一能做的就是启用完整的消息转发机制了。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nil ,Runtime则会发出 -doesNotRecognizeSelector: 消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation 对象并发送 -forwardInvocation:消息给目标对象。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if ([NSStringFromSelector(aSelector) isEqualToString:@"foo"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];//签名,进入forwardInvocation
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
Person *p = [Person new];
if([p respondsToSelector:sel]) {
[anInvocation invokeWithTarget:p];
}
else {
[self doesNotRecognizeSelector:sel];
}
}
method 的实现机制
Method 是一个objc_method结构体,objc_method是类的一个方法描述
SELOC在编译的时候,会根据方法签名(参数类型),生成一个用来区分这个方法的唯一的一个ID,本质上就是一个字符串,这个ID就是SEL类型的,在运行的时候是通过方法的ID来查找方法的,只要方法的签名相同,那么它们的ID都是相同的;不管是超类还是子类,还是其他类,只要签名相同那么ID就是一样的
不同的类可以拥有相同的selector, 不同的类的实例对象performSelector相同的selector时,会在各自的方法链表中根据selector去查找具体的方法实现IMP,然后用这个方法实现去执行具体的实现代码
1.在OC中想要得到方法的SEL可以直接使用@selector指示符,SEL act = @selector(setAage:)2.使用函数 NSSelectorFromString(@“setAge:”)3.SEL类型的方法名使用函数(NSString *)NSStringFromSelector(SEL)4.SEL转成函数指针 IMP imp_setAge = [self methodForSelector:@selector(setAge:)]
IMP一个函数指针,指向方法实现的首地址
通过取得IMP,我们可以跳过runtime的消息传递机制,直接执行IMP指向的函数实现,这样省去了runtime消息传递过程中所做的一系列查找操作,会比直接向对象发送消息的效率高一些
方法的调用流程
检测selector是否需要忽略
检查target是否nil,如果是nil就直接cleanup,然后return
在target的Class中根据selector去找IMP寻找IMP的过程:1、在当前class的方法缓存里面寻找(cache methodLists)2、找到了跳到对应的方法实现,没找到继续往下执行3、从当前class的方法列表里查找(methodLists),找到了添加到缓存列表里,然后跳转到对应的方法实现;没找到继续往下执行4、从superClass的缓存列表和方法列表里查找,直到找到基类为止,不论是在cache methodLists还是methodLists中找到IMP,都会先存入当前class的cache methodList在跳转到对应的方法实现5、以上步骤还找不到IMP,则进入到消息动态处理和消息转发流程
iOS中内省的几个方法?class方法和objc_getClass方法有什么区别
struct category_t {
const char *name; //名字
classref_t cls; //类的引用
struct method_list_t *instanceMethods;//实例方法列表
struct method_list_t *classMethods;//类方法列表
struct protocol_list_t *protocols;//协议列表
struct property_list_t *instanceProperties;//实例属性列表
struct property_list_t *_classProperties;//类属性列表
};
分类的实现原理是将category中的方法,属性,协议数据放在category_t结构体中,然后将结构体内的方法列表拷贝到类对象的方法列表中。
Category可以添加属性,但是并不会自动生成成员变量及set/get方法。因为category_t结构体中并不存在成员变量。通过之前对对象的分析我们知道成员变量是存放在实例对象中的,并且编译的那一刻就已经决定好了。而分类是在运行时才去加载的。那么我们就无法再程序运行时将分类的成员变量中添加到实例对象的结构体中。因此分类中不可以添加成员变量。
self与super的区别
weak的实现原理
Runtime应用
应用场景。
- 关联对象(Objective-C Associated Objects)给分类增加属性
- 方法魔法(Method Swizzling)方法添加和替换和KVO实现
- 消息转发(热更新)解决Bug(JSPatch)
- 实现NSCoding的自动归档和自动解档
- 实现字典和模型的自动转换(MJExtension)
在 iOS中可以直接调用某个对象的消息方式有两种:
一种是performSelector:withObject;
再一种就是NSInvocation。
第一种方式比较简单,能完成简单的调用。但是对于>2个的参数或者有返回值的处理,那performSelector:withObject就显得有点有心无力了,那么在这种情况下,我们就可以使用NSInvocation来进行这些相对复杂的操作
// 方法签名中保存了方法的名称/参数/返回值,协同NSInvocation来进行消息的转发
// 方法签名一般是用来设置参数和获取返回值的, 和方法的调用没有太大的关系
//1、根据方法来初始化NSMethodSignature
NSMethodSignature *signature = [ViewController instanceMethodSignatureForSelector:@selector(run:)];
// NSInvocation中保存了方法所属的对象/方法名称/参数/返回值
//其实NSInvocation就是将一个方法变成一个对象
//2、创建NSInvocation对象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置方法调用者
invocation.target = self;
//注意:这里的方法名一定要与方法签名类中的方法一致
invocation.selector = @selector(run:);
NSString *way = @"byCar";
//这里的Index要从2开始,以为0跟1已经被占据了,分别是self(target),selector(_cmd)
[invocation setArgument:&way atIndex:2];
//3、调用invoke方法
[invocation invoke];
//实现run:方法
- (void)run:(NSString *)method{
}
七、讲讲runloop
详细链接
runloop,运行循环,可使线程能随时处理时间但并不退出,这也就是手机app在运行时不会退出的原因。当没有事件时,runloop会进入休眠状态,有事件发生时,runloop再进行相应的处理事件。runloop可以让线程在需要做事的时候忙起来,不需要的时候让线程休眠。
Runloop的底层数据结构(NSRunLoop是CFRunLoop的封装):
CFRunLoop,RunLoop对象
Mode,运行模式
Source,输入源/事件源
Timer,定时源
Observer,观察者
系统默认注册了5个Mode常用的有3个 常见的几种Mode:
Default : App的默认Mode,通常主线程是在这个Mode下运行
UITracking: 界面跟踪Mode,用于ScrollView`追踪触摸滑动,保证界面滑动时不受其他Mode影响。
Common :并不是一个真的模式,它只是一个标记,如:被标记的 Timer可以在Default模式和UITracking下运行。
八、讲讲assign,weak区别,讲讲浅拷贝,深拷贝有什么区别
[NSArray copy] 浅copy
[NSArray mutableCopy] 深copy
[NSMutableArray copy] 深copy
[NSMutableArray mutableCopy] 深copy
九、将block和delegate的区别,__block有什么作用
1.当回调函数多于3个的时候,采用代理比较好
2.使用代码块容易造成循环引用,代理不会出现该问题
3.其他情况下优先考虑代码块
1.从源头上理解和区别block和delegate
delegate运行成本低,block的运行成本高。
block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除。delegate只是保存了一个对象指针,直接回调,没有额外消耗。就像C的函数指针,只多做了一个查表动作。
2.从使用场景区别block和delegate
有多个相关方法。假如每个方法都设置一个 block, 这样会更麻烦。而 delegate 让多个方法分成一组,只需要设置一次,就可以多次回调。当多于 3 个方法时就应该优先采用 delegate。当1,2个回调时,则使用block。
delegate更安全些,比如: 避免循环引用。使用 block 时稍微不注意就形成循环引用,导致对象释放不了。这种循环引用,一旦出现就比较难检查出来。而 delegate 的方法是分离开的,并不会引用上下文,因此会更安全些。
delegate回调返回的参数被限制在了 NS 类的范围内,数量也很有限(当然可以用直接调用方法的形式在绕过,并不推荐;也可以用 Array 套着传, 不过这样需要有文档支持,不然不够清晰,回调方法也需要独立的验证,故也不推荐)。
通过上面的知识,可以简单说他们的区别,block适合少数回调,需要运行内存,多次回调容易出现循环引用 delegate回调次数不限制,回调对象需要知道当前回调对象,delegate很安全不会引用上下文。
1.分析,ARC如果在块对象中使用了__block指定的变量,那么这个变量将会被copy到堆内存中,并且原变量也会指向这个堆内存中的空间
十、讲讲gcd是怎么开辟线程,底层是怎么实现的
同步执行方式是不创建新线程的,就在当前线程嗨。
线程按执行方式分为同步、异步,按队列管理分为串行并行,这样有四种组合,加上常说的主线程主队列,那么结合执行方式就有六种组合。
同步串行,不创建线程,所以还是在当前线程一个一个做
同步并行,不创建线程,所以就算是并行,也还是在当前线程一个一个做
异步串行,开辟多一条线程,任务在新开辟的一条线程里面一个一个做
异步并行,开辟多条线程,任务在新开辟的线程里面一起做
同步主队,阻塞
异步主队,同异步串行,因为主队就是串行,但是不开辟新线程,因为主线程是全局的单例的
2>GCD 只支持 FIFO(先进先出) 的队列,NSOperationQueue 可以很方便地调整执 行顺序、设置最大并发数量
在异步线程中下载很多图片,如果失败了,该如何处理?请结合RunLoop来谈谈解决方案
1> 重新下载图片
2>下载完毕, 利用 RunLoop 的输入源(输入源(input source)(传递异步事件))回到主线程刷新 UIImageVIUew
NSOperation是怎么处理最大并发数和线程依赖(用GCD实现)
最大并发数:用信号量控制最大数,如果并发数设置为4,那信号量为3,执行完了发送信号量不然就等着
线程依赖:使用调度组dispatch_group_create和GCD中的栅栏和wait也能实现同样的效果
十一、讲讲ARC的原理
自动的引用计数
如果放一张很大的图在项目加载,怎么才能检查到那个内存暴涨,哪个方法出现内存暴涨
利用Xcode自带的instrument检查内存占用情况,并定位内存不断增大的原因
点击profile。出现如下界面,选Allocations instrument
内存泄露 Instruments之Leaks
十二、讲讲有哪些区?全局变量,局部变量,静态变量存放在哪里
栈是先进后出的队列
堆都是动态分配的,没有静态分配的堆。栈有两种分配方式:静态分 配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动 态分配是有 alloc 函数进行分配的
局部变量:栈区
局部静态变量:静态区
全局变量:静态区的常量区
全局静态变量:静态区
内存分四个区:静态区,栈区,堆区,代码区
d、一个有10个整型数的数组 inta[10];
e、一个有10个指针的数组,该指针是指向一个整型数的 inta[10];
f、一个指向有10个整型数数组的指针 int(a)[10];
十三、有关HTTP、TCP/UDP
OSI(Open System Interconnection)模型定制的七层标准模型分别是:
物理层:物理层是OSI参考模型的最低层,它利用传输介质为数据链路层提供物理连接。它主要关心的是通过物理链路从一个节点向另一个节点传送比特流,物理链路可能是铜线、卫星、微波或其他的通讯媒介。它关心的问题有:多少伏电压代表1?多少伏电压代表0?时钟速率是多少?采用全双工还是半双工传输?总的来说物理层关心的是链路的机械、电气、功能和规程特性。主要有: 以太网 · 调制解调器 · 电力线通信(PLC) · SONET/SDH · G.709 · 光导纤维 · 同轴电缆 · 双绞线等
数据链路层:数据链路层是为网络层提供服务的,解决两个相邻结点之间的通信问题,传送的协议数据单元称为 数据帧。只要协议有:Wi-Fi( IEEE 802.11) · WiMAX( IEEE 802.16) ·ATM · DTM · 令牌环 · 以太网 ·FDDI · 帧中继 · GPRS · EVDO ·HSPA · HDLC · PPP · L2TP ·PPTP · ISDN·STP 等
网络层:网络层是为传输层提供服务的,传送的协议数据单元称为 数据包或分组。该层的主要作用是解决如何使数据包通过各结点传送的问题,即通过 路径选择算法( 路由)将数据包送到目的地。另外,为避免 通信子网中出现过多的数据包而造成 网络阻塞,需要对流入的数据包数量进行控制( 拥塞控制)。当数据包要跨越多个通信子网才能到达目的地时,还要解决网际互连的问题。IP (IPv4 · IPv6) · ICMP· ICMPv6·IGMP ·IS-IS · IPsec · ARP · RARP等
传输层:TCP · UDP · TLS · DCCP · SCTP · RSVP · OSPF 等
会话层:会话层主要功能是管理和协调不同主机上各种进程之间的通信(对话),即负责建立、管理和终止应用程序之间的会话。会话层得名的原因是它很类似于两个实体间的会话概念。例如,一个交互的用户会话以登录到计算机开始,以注销结束。
表示层:表示层处理流经结点的 数据编码的表示方式问题,以保证一个系统应用层发出的信息可被另一系统的应用层读出。如果必要,该层可提供一种标准表示形式,用于将计算机内部的多种 数据表示格式转换成 网络通信中采用的标准表示形式。数据压缩和加密也是表示层可提供的转换功能之一。
四次挥手:
所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。
(1)第一次挥手:客户端发送一个FIN包,用来关闭客户端和服务器的数据传送,客户端进入FIN_WAIT_1状态。
(2)第二次挥手:服务端收到FIN包后,发送一个ACK给客户端,服务端进入CLOSE_WAIT状态。
(3)第三次挥手:服务端发送一个FIN包,用来关闭服务端到客服端的数据传送,服务端进入LAST_ACK状态。
(4)第四次挥手:客户端收到FIN包,客户端进入TIME_WAIT状态,接着发送一个ACK包给服务端,服务端进入关闭状态,完成四次挥手。
实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,才能使用TCP/IP协议。
总结:
1.HTTP是应用层协议,定义的是传输数据的内容以及格式的规范。
2.TCP是底层通讯协议,定义的是数据传输和连接方式的规范。
3.Socket可以支持不同的传输层协议(TCP/UDP),当使用TCP协议进行连接时,该Socket连接就是一个TCP连接,Socket是发动机,提供了网络通信的能力
Socket是传输控制层接口,WebSocket是应用层协议。
一个完成的HTTP协议要包含三个部分: 请求行、请求头、请求体
请求行:主要包含请求方法、请求路径、HTTP协议版本
请求头:主要包含了对客户端环境的描述,客户端请求的主机地址信息。
请求体:客户端发给服务器的具体数据,比如文件/数据
HTTP协议规定:1个完整的HTTP响应中包含以下内容:
状态行:包含了HTTP协议版本、状态吗、状态码对应的英文名称HTTP/1.1 200 OK
响应头:包含了对服务器的描述,对返回数据的描述。
实体内容:服务器返回给客户端的具体数据(图片/html/文件...)
HTTP协议定义了很多方法对应不同的资源操作,其中最常用的是GET 和 POST 方法.
GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT、PATCH
增:PUT、删:DELETE、改:POST、查:GET
GET 和 POST 都是和服务器提交参数/通讯的一种方式。GET 参数不能太长<1024B POST 没有限制<4G
GET 不能上传文件, POST 可以上传文件
(1)JSON底层原理:遍历字符串中的字符,最终根据格式规定的特殊字符,比如{}、[]、:等进行区分,{}号表示字典,[]号表示数组,:号是字典的键和值的分水岭,最终仍是将JSON转化为字典,只不过字典中的值可能是“字典、数组或者字符串而已”。
在网络请求中如何提高性能
十四、数据持久化存储方案有哪些?
1)plist 文件(属性列表)
2)preference(偏好设置)
- NSKeyedArchiver(归档)
4)SQlite - CoreData
十五、计时器 NSTimer 循环引用
- 选择合适的时机手动释放timer(该方法并不太合理)
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.timer invalidate];
self.timer=nil;
}
- timer使用block方式添加Target-Action
- 给self添加中间件proxy
考虑到循环引用的原因,改方案就是需要打破这些相互引用关系,因此添加一个中间件,弱引用self,同时timer引用了中间件,这样通过弱引用来解决了相互引用,如图:
使用NSProxy解决NSTimer、CADisplayLink等循环引用
#import <Foundation/Foundation.h>
@interface YHProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
#import "YHProxy.h"
@implementation YHProxy
+ (instancetype)proxyWithTarget:(id)target
{
// NSProxy对象不需要调用init,因为它本来就没有init方法
YHProxy *proxy = [YHProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
}
@end
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[YHProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
十六、如何减小一个应用程序占用存储空间?
检查程序 去掉多余的 xib。iOS App Store 相关因素作为提交到 App Store 中 app 里的可执行文件是被加过密的。加密的副作用是可执行 文件的压缩效果没有之前的好了。Build Settings 编译选项,将 build setting 中的 Optimization Level 设置为 Fastest, Smallest [-Os]; 将 build setting 中 的 Strip Debug Symbols During Copy 设 置 为 YES(COPY_PHASE_STRIP = YES),这样可以减小编译出二进制文件的尺
寸。Target 针对较少的 CPUs 对程序指定的特定 CPU 类型做优化处理, 以生成相对于的可执行文件。不同的硬件,将运行不同的可执行代码。 虽然这样优化后的程序,只能针对某些设备运行,但是这大大减小可 执行程序的大小。要想只设定特定类型的CPUs,可以修改build setting 中的 Architectures,将其从 Standard $(ARCHS_STANDARD)修改为你希 望支持的列表中对应的特定类型 CPU。有效的 CPU 名称列在 Valid Architectures (VALID_ARCHS) build setting 中。请不要修改 Valid Architectures 设置项,最好由 Xcode 管理。尽量使用 8-bit 图片。使 用 8-bit 的 PNG 图片,比 32-bit 的图片能减少 4 倍的压缩率。由于 8-bit 的图片支持最多 256 种不同的颜色,所以 8-bit 的图片一般只应该用 于一小部分的颜色图片。例如灰度图片最好使用 8-bit。
十七、KVO、KVC
KVO
KVC
十八、怎么理解oc面向对象,oc有多重继承吗?
面向对象的三大特性:封装、继承和多态。
封装就是把数据及其操作的实现细节隐藏起来,对外公开接口(即“合理隐藏,合理暴露”)。这样可以保证数据安全性,外部不能随便访问类的成员变量,只能通过(setter,getter)方法访问。
继承的作用是抽取出了重复的代码,建立了类和类之间的联系
多态:由继承演变而来,父类指针指向子类对象,即同一类型的对象调用同一方法,表现出不同行为
oc不能多继承,但是可以通过其他方法实现“多继承”效果
方法一:采用组合的模式来代替继承模式。
方法二:
虽然OC在语法上禁止类使用多继承,但是在协议的遵守上却允许使用多继承。所以可以用协议来实现多继承。但是协议只能提供接口,而没有提供实现方式,如果只是想多继承基类的接口,那么遵守多协议无疑是最好的方法,而既需要多继承接口,又要多继承其实现,那么协议是无能为力了。多协议遵守比较简单,具体的实现方式这里就不讲了
方法三:给NSObject添加category
因为NSObject是所有类的父类,给NSObject添加类别就相当于给所有的类添加了这些方法,所有的类都可以使用这些方法。
十九、项目中有用到哪些组件化
1、常⽤的三种方案
1.1 URL Scheme
使 URL 处理本地的跳转
通过中间层进⾏注册 & 调⽤ (load方法里把被调用者注册到中间层)
-
代表是MGJRouter注册表⽆需使用反射 非懒加载 / 注册表的维护 / 参数
MGJRouter就一个单例类,使用前需要通过注册组件,调用方通过URL调用服务方页面,通过路由表的映射关系进行关联,调用方可以传入复 杂的参数、对象
实现原理:
App启动时实例化各组件模块,然后这些组件向ModuleManager注册Url,有些时候不需要实例化,使用class注册
当组件A需要调用组件B时,向ModuleManager传递URL,参数跟随URL以GET方式传递,类似openURL。然后由ModuleManager负责调度组件B,最后完成任务。
1.2 Target - Action
抽离业务逻辑
通过中间层进行调⽤
中间层使⽤ runtime 反射
中间层代码优化
CTMediator
原理是通过oc的runtime、category特性动态获取模块,例如通过NSClassFromString获取类并创建实例,通过performSelector + NSInvocation动态调用方法。
实现原理:
1、利用分类为路由添加新接口,在接口中通过字符串获取对应的类
2、通过runtime创建实例,动态调用实例的方法
1.3 Protocol - Class 匹配
增加 Protocol Wrapper层 (中间件先注册Protocol和Class对应关系,将protocol和对应的类进行字典匹配)
中间件返回 Protocol 对应的 Class,然后动态创建实例
解决硬编码的问题
- BeeHive
- protocol比较典型的三方框架就是阿里的BeeHive。BeeHive借鉴了Spring Service、Apache DSO的架构理念,采用AOP+扩展App生命周期API形式,将业务功能、基础功能模块以模块方式以解决大型应用中的复杂问题,并让模块之间以Service形式调用,将复杂问题切分,以AOP方式模块化服务。
- BeeHive 核心思想
- 1、各个模块间调用从直接调用对应模块,变成调用Service的形式,避免了直接依赖。
- 2、App生命周期的分发,将耦合在AppDelegate中逻辑拆分,每个模块以微应用的形式独立存在。
二十、id和NSObject*的区别
1、id是objc_object的结构体指针,定义是typedef struct objc_object id,所有的oc对象都可以用id指向,而且在编译阶段不作类型检查。id对象调用这个对象存在的方法在编译阶段都不会报错,但是调用不存在的方法会。
2、NSObject指向的必须是NSObject的子类,调用的方法也必须是NSObject子类的方法,否则必须作强制的类型转换。
3、不是所有的oc对象都是NSObject的子类,比如说有一些继承自NSProxy,NSObject*可指向的对象是id的子集。