UIButton继承关系: UIControl -> UIView -> UIResponder -> NSObject
Model View Controller(模型 视图 控制器):Control 负责初始化 Model,并将 Model 传递给 View 去解析展示
MVP分为 Model(模型)、View(视图)、Presenter(表示器)三部分组成,Presenter层,其负责调控View与Model之间的间接交互
Model(数据层)、ViewController/View(展示层)、ViewModel(数据模型):
MVVM模式主要是减轻Controller层或者View层的压力,实现更加清晰化代码。通过对ViewModel层的封装:封装业务逻辑处理,封装网络处理、封装数据缓存等
iOS代理模式: 包含【委托对象】、【代理对象】和【协议】
[协议]:用来指定代理需要做的事
[委托对象]:根据协议,指定代理去完成什么功能
[代理对象]:根据协议,完成委托方需要实现的功能
iOS单例模式:确保某一个类只有一个实例,不能通过其它类来初始化自己,在程序运行的过程中只有一份,节约内存
iOS观察者模式:定义对象间一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新 NSNotificationCenter和KVO
适配器模式 && 策略模式 && 装饰模式 && 工厂模式
NSTimer受RunLoop模式的影响:
timerWithTimeInterval: 不会自动加入运行循环,需要我们手动指定模式,并手动加入运行循环
scheduledTimerWithTimeInterval: 自动以NSDefaultRunLoopModel模式加入运行循环
CADisplayLink:在屏幕每次刷新时执行一次,创建的displayLink添加到runloop中,否则定时器不会执行
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
解决NSTimer循环引用:
1.使用NSTimer提供的API,在block中执行定时任务
2.通过消息转发的方法的方式,声明弱代理
3. #pragma mark - 第一种方法:合适的时机关闭定时器
- (void)didMoveToParentViewController:(UIViewController *)parent
{
if (!parent) {
[_timer invalidate];
_timer = nil;
}
}
4.使用NSProxy,此方法无需经过消息转发,效率更高
线程保活:使用端口(addPort:forMode:)保活子线程
同步与异步的区别:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync - %@", [NSThread currentThread]);
});
dispatch_sync 立即阻塞当前的主线程,然后把 Block 中的任务放到 main_queue 中, main_queue 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了
Runloop的作用:保持程序的持续运行、随时处理各种事件、节省cpu资源(没事件休息释放资源)、渲染屏幕UI
Mode:主要用来指定事件在运行时循环的优先级
- (void)setAbcString:(NSString *)abcString {
if (_abcString != abcString) {
[_abcString release];
_abcString = [abcString retain];
}
}
A = A+B B = A-B A = A-B
NSOperation是一个抽象类,也就是说它并不能直接使用,而是应该使用它的子类 NSInvocationOperation && NSBlockOperation && 自定义继承NSOperation的子类
NSOperation的使用常常是配合NSOperationQueue来进行的
NSOperationQueue操作队列之中,一旦添加到队列,操作就会自动异步执行(注意是异步)
如果没有添加到队列,而是使用start方法,则会在当前线程执行
提供了GCD不好实现的:1.最大并发数,2.暂停和继续,3.取消所有任务,4.依赖关系
- (id)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *hitView = [super hitTest:point withEvent:event];
if (hitView == self) {
return nil;
} else {
return hitView;
}
}
触摸hitTest的调用顺序:touch -> UIApplication -> UIWindow -> UIViewController.view -> subViews -> ....-> 合适的view
事件的传递顺序:view -> superView ...-> UIViewController.view -> UIViewController -> UIWindow -> UIApplication -> 事件丢弃
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
}
KVO全称KeyValueObserving,是苹果提供的一套事件通知机制。允许对象监听另一个对象特定属性的改变,并在改变时接收到事件。由于KVO的实现机制,所以对属性才会发生作用,一般继承自NSObject的对象都默认支持KVO。
dispatch_barrier_async:会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中 针对全局队列无效
dispatch_after:延时执行方法
dispatch_once: 一次性代码(只执行一次)
dispatch_apply:快速迭代方法
dispatch_group:
dispatch_group_notify
dispatch_group_enter
dispatch_group_leave
dispatch_semaphore:
dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量
dispatch_semaphore_signal:发送一个信号,让信号总量加 1
dispatch_semaphore_wait:可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。
UIView为CALayer提供内容,以及负责处理触摸等事件,参与响应链
CALayer负责显示内容contents
Off-Screen Rendering:离屏渲染,分为CPU离屏渲染和GPU离屏渲染两种形式。GPU离屏渲染指的是GPU在当前屏幕缓冲区外新开辟一个缓冲区进行渲染操作,应当尽量避免的则是GPU离屏渲染
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
iOS中的内存大致可以分为代码区,常量区,全局/静态区,堆区,栈区;
栈区是由编译器自动分配并释放的,栈区地址从高到低分配;
堆区使用alloc分配内存,堆区地址是从低到高分配;
https://www.cnblogs.com/dins/p/ios-nei-cun-fen-pei-yu-fen-qu.html
结构体一般采用内存对齐的方式分配,其大小为结构体最宽成员大小的整数倍
bool 1个字节 char 1个字节
int 4个字节 float 4个字节
double 8个字节 long 8个字节 class isa 8个字节
autoreleasePool什么时候创建的,里面的对象又是什么时候释放的?
App启动后,苹果在主线程 RunLoop 里注册了两个 Observer,其回调都是 _wrapRunLoopWithAutoreleasePoolHandler()
第一个 Observer 监视的事件是 Entry(即将进入Loop),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池
第二个 Observer 监视了两个事件: BeforeWaiting(准备进入休眠) 时调用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 释放旧的池并创建新池;Exit(即将退出Loop) 时调用 _objc_autoreleasePoolPop() 来释放自动释放池
手动autoreleasePool:@autoreleasepool {}方式手动创建autoreleasepool对象,出了autoreleasepool的大括号就释放了
/**直接进行消息发送机制: objc_msgSend **/
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
/**使用Runloop方法 **/
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
waitUntilDone : YES, 需要等待这个selector 执行完之后,才会往下执行,相当于同步
waitUntilDone : NO, 不用等待这个selector,就会往下执行,相当于异步
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread waitUntilDone:(BOOL)wait;
wait:要执行的aSelector方法,是否马上执行。
如果设置为YES:先让主线程运行aSelector中的一些操作,之后再进行当前线程中的操作
设置为NO:先进行当前线程中的操作,之后让主线程运行aSelector中的一些操作。
如果,当前线程就是主线程,那么aSelector方法会马上执行。
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
+ (void)load {} runtime加载类和分类的时候调用 直接拿到函数地址,直接调用
1.先调用父类的load
2.再调用子类的load
3.再调用分类的load,先编译先调用
+ (void)initialize{} 当前类第一次接收到消息的时候调用 通过消息发送方式
1.先调用父类的initialize
2.再调用子类的initialize
3.子类未实现initialize方法时,会调用父类initialize方法
// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
// 全局队列
dispatch_get_main_queue()
// 全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
同步执行任务:
dispatch_sync(queue, ^{
// 这里放同步执行任务代码
});
异步执行任务:
dispatch_async(queue, ^{
// 这里放异步执行任务代码
});
栅栏函数:
放全局并发队列跟异步是同一个效果 A、B操作、栅栏栅栏栅栏操作、C、D操作并发执行
放自己创建的并发队列则有效 A、B操作并发执行 栅栏栅栏栅栏操作 C、D操作并发执行
dispatch_queue_t queue = dispatch_queue_create("testqueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue,^{
for (int i = 0; i < 3; i++) {
CHSLOG(@"A操作");
}
});
dispatch_async(queue,^{
for (int i = 0; i < 3; i++) {
CHSLOG(@"B操作");
}
});
dispatch_barrier_async(queue,^{
for (int i = 0; i < 3; i++) {
CHSLOG(@"栅栏栅栏栅栏操作");
}
});
dispatch_async(queue,^{
for (int i = 0; i < 3; i++) {
CHSLOG(@"C操作");
}
});
dispatch_async(queue,^{
for (int i = 0; i < 3; i++) {
CHSLOG(@"D操作");
}
});
类扩展和分类的区别:
类扩展:编译的时候会把属性和方法一起放到类中
分类:利用Runtime把属性和方法放到原类中,同名方法会覆盖掉原来类中的方法,优先调用后编译的方法
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__Block_byref_b_0 *b;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
}
struct __Block_byref_b_0 {
void *__isa;
__Block_byref_b_0 *__forwarding;
int __flags;
int __size;
int b;
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
}
block = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a)
block->FuncPtr(block);
获取到的基础变量的值在block初始化的时候已经确定了,block外部的变量a在后续无论做什么操作,都不会影响block内部保存的变量a
UIView 继承 UIResponder,而 UIResponder 是响应者对象,可以对iOS 中的事件响应及传递,CALayer 没有继承自 UIResponder,所以 CALayer 不具备响应处理事件的能力
self和super底层实现原理:
id objc_msgSend(id theReceiver, SEL theSelector, ...) 当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;
id objc_msgSendSuper(struct objc_super *super, SEL op, ...) 而当使用 super 时,则从父类的方法列表中开始找,然后调用父类的这个方法
@synthesize 是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法
@dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成
oc调用js代码两种方式:
1.通过webVIew调用 webView stringByEvaluatingJavaScriptFromString: 调用
2.通过JSContext调用[context evaluateScript:];
物理层 数据链路层 网络层 传输层(TCP协议) 会话层 表示层 应用层(HTTP协议)
TCP:面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。
UDP:面向非连接、传输不可靠、用于传输少量数据(数据包模式)、速度快。
- (void)viewDidLoad{
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(),^{
NSLog(@"deadlock");
});
}
在主线程中运用主队列同步,也就是把任务放到了主线程的队列中。
同步对于任务是立刻执行的,那么当把任务放进主队列时,它就会立马执行,只有执行完这个任务,viewDidLoad才会继续向下执行。
而 viewDidLoad 和任务都是在主队列上的,由于队列的先进先出原则,
任务又需等待viewDidLoad执行完毕后才能继续执行,
viewDidLoad 和这个任务就形成了相互循环等待,就造成了死锁。
dispatch_queue_t serialQueue = dispatch_queue_create("test",DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue,^{
dispatch_sync(serialQueue,^{
NSLog(@"deadlock");
});
});
外面的函数无论是同步还是异步都会造成死锁。
这是因为里面的任务和外面的任务都在同一个 serialQueue 队列内,又是同步,这就和上边主队列同步的例子一样造成了死锁;
解决方法也和上边一样,将里面的同步改成异步dispatch_async
或者将 serialQueue 换成其他串行或并行队列,都可以解决
dispatch_queue_t serialQueue = dispatch_queue_create("test",DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("test2",DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue,^{
dispatch_sync(serialQueue2,^{
NSLog(@"deadlock");
});
});
这样是不会死锁的,并且 serialQueue和serialQueue2是在同一个线程中的。
5、自旋锁:OSSpinLock
6、互斥锁:pthread_mutex、@synchronized、NSLock、NSConditionLock 、NSCondition、NSRecursiveLock、dispatch_semaphore_t os_unfair_lock
对称加密(3DES|AES|DES) 加密和解密使用同一个密钥
非对称加密(RSA) 需要两个密钥:公开密钥(publickey) 和私有密(privatekey)
如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密。
如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。
原始报文/摘要/数字签名:原始报文即为经过加密处理的报文,当原始报文经过了一些hash算法处理之后就成为了数字摘要,再经过RSA处理之后,当数字摘要经过密钥加密之后就成为了数字签名
数字签名的作用主要是:确保发送的报文没有被篡改
class方法和object_getClass方法的区别:
object_getClass(obj): 返回的是obj的isa指针
[obj class]: obj为实例对象 返回的obj对象中的isa指针;
obj为类对象(包括元类和根类以及根元类) 返回的结果为调用者本身;
Class Extension在编译的时候,它的数据就已经包含在类信息中;
Category是在运行时,才会将数据合并到类信息中; 为一个类添加额外的原来没有变量,方法和属性
iOS和h5之间的交互:
1.利用UIWebView交互 通过调用JS来实现OC调用JS stringByEvaluatingJavaScriptFromString
通过协议拦截实现JS调用OC webView:shouldStartLoadWithRequest:navigationType:
2.JavaScriptCore.framework原生框架
OC调用JS evalueScript()
3.WKWebView的交互 JS调用OC [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"Share"];
OC调用JS evaluateJavaScript:
4.WebViewJavascriptBridge
1、客户端APP将苹果APNS服务器返回的devicetoken发送给我们自己的服务端程序;
2、推消息时,我们服务器根据devicetoken向苹果APNS服务器发送消息;
3、苹果APNS服务器将消息根据devicetoken发送给客户端;
iOS汇编相关知识:
1.寄存器: ARM64 有34个寄存器,包括31个通用寄存器、SP、PC、CPSR
2.通用寄存器:x0~x28(64位的) (w0~w28(32位)对应的的x0~x28的低32位) ,x0~x7一般是用来存储函数的参数,更多参数,则用堆栈来传递,x0一般用作函数的返回值
3.PC: 程序计数器,存储着当前执行的指令
4.FP:栈顶指针,指向一个栈帧的顶部,当函数发生跳转时,会记录当时的栈的起始位置。
5.SP:栈指针(也称为栈底指针),指向栈当前的位置。
6.CPSR:状态寄存器
NZCV是状态寄存器的条件标志位,分别代表运算过程中产生的状态,其中:
N, negative condition flag,一般代表运算结果是负数
Z, zero condition flag, 指令结果为0时Z=1,否则Z=0;
C, carry condition flag, 无符号运算有溢出时,C=1。
V, oVerflow condition flag 有符号运算有溢出时,V=1。
7.LR:通常称X30为程序链接寄存器,保存子程序结束后需要执行的下一条指令
8.wzr:32位零寄存器
9.xzr:64位零寄存器
mov X1,X0 将寄存器X0的值传送到寄存器X1
add X0,X1,X2 寄存器X1和X2的值相加后传送到X0
sub X0,X1,X2 寄存器X1和X2的值相减后传送到X0
and X0,X0,#0xF X0的值与0xF相位与后的值传送到X0
orr X0,X0,#9 X0的值与9相位或后的值传送到X0
eor X0,X0,#0xF X0的值与0xF相异或后的值传送到X0
ldr X5,[X6,#0x08] ld:load X6寄存器加0x08的和的地址值内的数据传送到X5
ldp x29, x30, [sp, #0x10] ldp :load pair 一对寄存器, 从内存读取数据到寄存器
str X0, [sp, #0x8] st:store, str:往内存中写数据(偏移值为正); X0寄存器的数据传送到SP+0x8地址值指向的存储空间
stur w0, [x29, #-0x8] 往内存中写数据(偏移值为负)
stp x29, x30, [sp, #0x10] store pair,存放一对数据, 入栈指令
cmp 比较指令,影响程序状态寄存器CPSR
b 跳转指令,可带条件跳转与cmp配合使用
bl 带返回的跳转指令, 返回地址保存到LR(X30)
ret:函数返回,相当于return
叶子函数:
void test(){
int a = 2;
int b = 3;
}
sub sp, sp, #16 ; =16,sp栈顶指针上移16个字节
.cfi_def_cfa_offset 16
mov w8, #2 ;将2存入w8寄存器
str w8, [sp, #12] ;将w8寄存器的数据存入到sp下移12个字节的所在位置下面的4字节
mov w8, #3 ;将3存入w8寄存器
str w8, [sp, #8] ;将w8寄存器的数据存入到sp下移8个字节的所在位置下面的4字节
add sp, sp, #16 ; =16,sp栈顶指针下移16个字节,恢复到初始位置
ret
非叶子函数,除了叶子函数,其他函数叫非叶子函数:
void excute(){
int a = 4;
int b = 5;
test();
}
void test(){
int a = 2;
int b = 3;
}
Mach-O包括以下几种类型:
OBJECT,指的是.o或.a文件(目标文件)
EXECUTE,指的是IPA拆包后的文件(可执行文件)
DYLIB,指的是.dylib或.framework文件(动态库文件)
DYLINKER,指的是动态连接器(动态链接器文件)
DSYM(符号表),指的是有保存符号信息用于分析闪退信息的文件(符号表文件)
加载过程:
1.把可执行文件加载到内存中,并从它中分析出dyld的路径,再把dyld加载到内存中,最后dyld递归加载所有的动态链接库dylib
dyld可以用来加载以下三种类型的Mach-O文件
MH_EXECUTE
MH_DYLIB
MH_BUNDLE
.p12 文件:Mac本地生成的钥匙对私钥。由于私钥是本地私有的,.p12将私钥导出给其他团队成员使用
当一个对象obj被weak指针指向时,这个weak指针会以obj作为key,weak指针的地址数组作为value,被存储到sideTable类的weak_table这个散列表上对应的一个weak指针数组里面。
当一个对象obj的dealloc方法被调用时,Runtime会以obj为key,从sideTable的weak_table散列表中,找出对应的weak指针列表,然后将里面的weak指针逐个置为nil。
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
(lldb) e id $hgView = (id)0x7fdfc66127f0
(lldb) e (void)[$hgView setBackgroundColor:[UIColor redColor]]
(lldb) e (void)[CATransaction flush]
iOS UITableView 的 优化:
1.Tableview 懒加载、Cell 重用
2.高度缓存(因为 heightForRowAtIndexPath: 是调用最频繁的方法)
3.数据处理:
3.1.使用正确的数据结构来存储数据
3.2.数据尽量采用局部的 section,或 cellRow 的刷新,避免 reloadData
3.3.大量数据操作时,使用异步子线程处理,避免主线程中直接操作
4.异步加载图片:SDWebImage 的使用:
4.1.使用异步子线程处理,然后再返回主线程操作
4.2.图片缓存处理,避免多次处理操作
5.按需加载内容,只显示目标范围内的 Cell 内容
6.减少Subviews 的数量
7.不要动态添加视图或移除视图,用hidden显示(隐藏)Subviews
同步执行(sync):
1.同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行
2.只能在当前线程中执行任务,不具备开启新线程的能力
异步执行(async)
1.异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务
2.可以在新的线程中执行任务,具备开启新线程的能力
iOS编译的过程:
预处理:处理以#开头的命令,删除注释,解开宏定义等
编译:词法分析、语法分析、语义分析、中间代码生成与优化,最终生成汇编代码
汇编:将汇编代码翻译成机器码,生成.o目标文件
链接:将多个.o目标文件和其他函数库链接成可执行文件
网友评论