记录:

作者: 许久__ | 来源:发表于2022-06-14 15:16 被阅读0次
    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目标文件和其他函数库链接成可执行文件
    

    相关文章

      网友评论

          本文标题:记录:

          本文链接:https://www.haomeiwen.com/subject/ivezmrtx.html