OC基础快速恢复2

作者: 平安喜乐698 | 来源:发表于2017-11-18 14:46 被阅读3440次
    目录
        知识点  
            1.类别和扩展
            2.@修饰符
            3.const ,  #define ,  typedef,extern,static
            4.野指针和空指针的区别
            5.self.name和_name的区别
            6.id与instancetype与void *的区别
            7.nil、Nil、Null、NSNull的区别
            8.#include 和 #import 的区别
            9.循环引用
        单例
        多线程
        block
        KVC
        KVO
        内存管理机制
        Category
        架构模式
    
    0. 知识点
    1. 类别和扩展
    类别 Category
        只能添加方法,不能添加属性(因为添加属性时,不会自动生成set、get方法。可使用runtime动态运行时机制来添加,但只有在运行时才存在)
        级别高于普通类,会覆盖普通类中同名的方法(因为先编译普通类,再编译类别)。若两个类别都实现了,则决定于头文件的添加顺序(排在下边的优先级高)。
    
    扩展
        只存在于.m文件中.    
        @interface Person(){
            NSString *name;
        }
        @property (nonatomic,readwrite) NSArray *contentArray; 
        -(void)run; 
        @end
    
    1. @修饰符
    @property修饰符
    例:
         @property (nonatomic,strong) NSArray *contentArray; 
    
    “访线指引”
         访问权限相关   :readwrite、readonly
         线程安全相关   :nonatomic、atomic(注意不写时:默认为atomic。原子性:一个操作要么执行要么不执行且执行中不会被打断,使用自旋锁不允许多线程同一时间访问,耗资源降低性能,且不一定线程安全。只会对set上锁,get没必要)
         指定方法名     :setter=、getter=
         引用计数相关   :copy、assign、weak、strong、retain
    默认:(readwrite,assign, atomic)
    
         copy
         NSString/NSDictionary/NSArray使用copy,而不是strong(将NSMutableArray对象赋值给NSArray后,修改前者,后者内容也变了。)    block使用copy,将block的值从栈区复制到堆区,改变block的作用域
         assign   :直接赋值(修饰基本数据类型、结构体、枚举---非类类型)
         weak     :弱引用,引用计数不+1  (仅修饰类类型,防止循环引用)(dele避免循环引用造成内存泄漏;控件---VC强引用self.view强引用subViews数组强引用控件,所以可以使用weak修饰)
         strong   :强引用,引用计数+1(仅修饰类类型,除了NSArray/NSDictionary/NSArray/.../block外的类类型)
    
    
    深拷贝/浅拷贝
         深拷贝:拷贝对象的值
         浅拷贝:拷贝对象的指针
         copy出来的对象:不可变。      (copy不可变类型:浅拷贝,copy可变类型:深拷贝)
         mutableCopy出来的对象:可变。 (永远都是深拷贝)   
         错误观点:copy是浅拷贝,mutableCopy是深拷贝。  当用copy可变数组时
    
    不完全拷贝/ 完全拷贝
         不完全拷贝(默认,拷贝的内容中元素是类类型,则只拷贝该元素的指针)  
          完全拷贝(需要覆写copyWithZone:手动返回新实例)
    
    修饰属性和方法的使用范围
        @public      : 所有地方都能使用
        @private     : 仅本类可用
        @protected   : 本类及子类
        @package     : 本应用程序包
    
    修饰协议中方法
        @optional    : 协议可选实现方法
        @required    : 协议必须实现方法
    
    修饰属性
        @dynamic 属性;
          其getter和setter方法编译器是不会自动生成,需手动实现,否则使用时崩溃.
        @synthesize 属性;(默认情况下编译器自动生成)
          其getter和setter方法编译器是会自动生成,不需手动实现。
    
    修饰类
        @class 类名;
        作用:用于在.h中提前声明一个类,提高编译效率;.m依旧要引入该类#import""
    
    1. const , #define , typedef,extern,static
    const    用来定义一个常量(存放在常量表中,编译时检查数据类型)
          const int a=10; int const a=10;   const与数据类型互换无影响
          const Person *p; *p不可变  p可变      Person *const p;  p不可变  *p可变
    
    #define  
        宏定义(预编译指令,预处理阶段直接替换,不存在于常量表中),将前者替换成后者,编译时不检查数据类型
        #define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
    typdedef 
        给已有类型起别名(1.便捷:枚举,结构体,block;2.安全:给系统类型重命名),检查数据类型
        typedef int MyIn;  // typedef 旧 新;
    extern   
        修饰全局变量且没有用static修饰时,其作用域为整个项目文件
        // 定义
        int gAge =10;
         // 其他文件中获取
        extern int gAge; 
        NSLog(@"%d", gAge);
    static
          修饰局部变量时:程序运行期间只会初始化一次 并且 直到整个程序结束时才被销毁
          static int age = 20;
          修饰全局变量时:作用域仅限于本类
    
    
    
    比较
         const和#define都可以定义常量,区别:
             处理时间不同:#define预编译阶段(编译之前),const编译阶段
             #define不能进行类型检测,const可以
             #define可以定义函数,const只能定义常量
             大量#define会造成编译时间过长
    
         typdedef与#define的区别
              typedef会对语法进行检测,#define直接替换
    
    使用
    
    应用的所有地址
    方法1:新建.h文件(需要在pch中导入)
        在.h中 
        // 开发中的API_URL使用(避免此文件外被改动)
        static  NSString * const loginURL = @"/login";
        // 以下在这里错误,loginURL依然可以被改成其他值
        static  NSString const * loginURL = @"/login";
    方法2:新建.h.m文件(需要在pch中导入)
        在.h中 
        extern NSString * const loginURL;
        在.m中 
        NSString * const loginURL = @"name";
    
    1. 野指针和空指针的区别
    野指针和空指针的区别
        野指针:
            指针指向了一个被释放的对象(僵尸对象,造成的原因:过多地release对象)(所指向的地址有个“标识”:标识此为僵尸对象)
            只要给野指针发送消息,程序就会报错(为了避免,对象释放后,设置为nil)。
        
        空指针:
            指针指向nil(指针设置为nil)。
            给空指针发送消息不会报错。[nil Method];    // 不会崩
    
    1. self.name和_name的区别
    self.name和_name的区别:
        self.name会调用setget方法修改变量,而_name会直接修改变量。
    
    self.name=nil和[self.name release]的区别:
        self.name是通过set方法将name属性置为nil,[name release] 只是将name的引用计数减1,此时若name的引用计数不为0,还可以访问。
    
    1. id与instancetype与void *的区别
    id与instancetype与void *的区别:
        id可以指向任何类型的对象(不需要*)
        instanceType只能作为返回值返回和该方法所在类相同类型的对象
        void*指无类型指针(等同于id),指向任意类型的对象
    
    1. nil、Nil、Null、NSNull的区别
    nil、Nil、Null、NSNull的区别:
        nil    :表示空对象,调用nil不会崩溃
        Nil    :表示空类
        Null   :表示空指针,不指向任何内存地址
        NSNULL :表示一个空的集合对象
    

    8.#include 和 #import 的区别

    #include 不能(会多次导入)
    #import 不会重复导入头文件
        #import<> 包含系统头文件(从系统库目录中查找)
        #import"" 包含自定义头文件(查找自定义头文件)
    
    
    1. 循环引用
    block
        当block作为属性(copy),且block内部引用self,造成循环引用。
        解决:
        __weak BViewController *weakSelf = self;
        self.block = ^{
            NSLog(@"%@",weakSelf.name); 
        };
    
    NSTimer
        当timer作为属性,且设置self为target,造成循环引用
        解决:使用weak修饰
    
    delegate
        当B是A的属性,且B的delegate属性是A,造成循环引用
        解决:
        delegate属性用weak修饰
    
        NSObject
            +(void)initialize{[super initialize];}
            第一次使用该类时调用(会在init方法前调用)(类别优先级高)
            +(void)load{}
            应用启动后会 加载所有类的load方法(最先,会在main之前)(类别优先级高)
    
    延迟加载:
        重写get方法,用到时才加载
        避免瞬时内存过高
    异步加载:
        避免线程堵塞
    

    delegate

    // dele使用weak:为了避免循环引用
    @property (nonatomic,weak) id delegate;
    
    
    例:
    UITableView的delegate和dataSource都是代理
      delegate提供交互
      dataSource提供数据源
    
    代理和block的区别:
        相同点:都能传递值;都应避免循环引用;都应在使用前判断是否实现;
        不同点:代理需要创建协议,用weak修饰dele属性,实现dele方法。block用copy修饰
    
    View
        // 构造(初始化)
        -(instancetype)init{}   
        -(instancetype)initWithFrame:(CGRect)frame;
        -(void)delloc{};        // 销毁
    

    指针

         int a[10];           // 10个int 元素
         int *a[10];          // 10个指向int的指针 元素
         int (*a)[10];        // 指向10个int元素数组 指针
         int (*a[10])(int);   // 10个指向函数(返回int参数int)的指针 元素
    
    1. 单例
    单例
      程序运行期间(从点击App开始运行到关掉App结束运行),该类只会创建一个实例(对象指针存在于静态区,对象在堆中所占的空间 只会在程序终止后才会被释放)。
    
                                     使用
    

    YTAccount.h

    #import <Foundation/Foundation.h>
    @interface YTAccount : NSObject<NSCopying,NSMutableCopying>
    +(instancetype)sharedAccount;
    @end
    

    YTAccount.m

    #import "YTAccount.h"
    
    @implementation YTAccount
    static YTAccount *_sharedAccount;
    
    // 用于外部访问
    +(instancetype)sharedAccount{
        return [self new];
    }
    
    
    // 调用alloc会调用本方法(若不实现,用allocWithZone多次创建对象得到的不是单例)
    +(instancetype)allocWithZone:(struct _NSZone *)zone{
    /*
    // 方法一
        // 线程锁(防止多线程同时调用)
        @synchronized (self) {
            if(!_sharedAccount){
                _sharedAccount=[super allocWithZone:zone];
            }
        }
        return _sharedAccount;
    */
        
    // 方法二
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            //
            if(!_sharedAccount){
            
                _sharedAccount=[super allocWithZone:zone];
            }
        });
        return _sharedAccount;
    }
    
    // 避免使用copy mutableCopy方法时再次创建
    -(id)copyWithZone:(NSZone *)zone{
        return _sharedAccount;
    }
    -(id)mutableCopyWithZone:(NSZone *)zone{
        return _sharedAccount;
    }
    @end
    
                                 优化使用(方案一)
    

    放在pch中

    #define shareH(name) +(instancetype)share##name;
    
    #if __has_feature(objc_arc)
    
    #define shareM(name) static id _instance;\
    +(instancetype)allocWithZone:(struct _NSZone *)zone\
    {\
    static dispatch_once_t onceToken;\
    dispatch_once(&onceToken, ^{\
    _instance = [super allocWithZone:zone];\
    });\
    return _instance;\
    }\
    \
    +(instancetype)share##name\
    {\
    return [[self alloc]init];\
    }\
    -(id)copyWithZone:(NSZone *)zone\
    {\
    return _instance;\
    }\
    \
    -(id)mutableCopyWithZone:(NSZone *)zone\
    {\
    return _instance;\
    }
    #else
    #define singleM(name) static id _instance;\
    +(instancetype)allocWithZone:(struct _NSZone *)zone\
    {\
    static dispatch_once_t onceToken;\
    dispatch_once(&onceToken, ^{\
    _instance = [super allocWithZone:zone];\
    });\
    return _instance;\
    }\
    \
    +(instancetype)share##name\
    {\
    return [[self alloc]init];\
    }\
    -(id)copyWithZone:(NSZone *)zone\
    {\
    return _instance;\
    }\
    -(id)mutableCopyWithZone:(NSZone *)zone\
    {\
    return _instance;\
    }\
    -(oneway void)release\
    {\
    }\
    \
    -(instancetype)retain\
    {\
    return _instance;\
    }\
    \
    -(NSUInteger)retainCount\
    {\
    return MAXFLOAT;\
    }
    #endif
    

    使用

      .h中  shareH(类名)
      .m中  shareM(类名)
    
                                 优化使用(方案二)
    
    实现一个单例基类
    需要单例时继承即可
    
    2. 多线程

    2.1 概念

    程序、进程、线程、多线程
        启动一个应用程序后,会至少创建一个进程,该进程会创建一个主线程。
    

    进程

    1、简介
     (1)一个进程是指在系统中正在运行的一个应用程序。
     (2)进程间相互独立,每个进程均运行在其专用且受保护的内容空间内。
     (3)通过“活动监视器”可以查看mac系统中所开启的进程。
    2、进程通信
     单机系统中进程通信有四种形式:主从式、会话式、消息或邮箱机制、共享存储区方式。
        主从式例子:终端控制进程和终端进程。
        会话式例子:用户进程与磁盘管理进程之间的通信。
    

    线程

    简介
    (1)线程(Thread),也称作轻量级进程。线程是进程的组成部分,一个进程可以拥有多个线程(至少有一个主线程)。
    (2)线程在程序中是独立的、并发的执行流,当进程被初始化后,主线程就被创建了。
    (3)可以在进程内创建多条顺序执行流,这些顺序执行流就是线程,每条线程是相互独立的。
    (4)线程是独立运行的,单个线程他是不知道进程中是否存在其他的线程,线程的执行是抢占式的。(当前运行的线程在任何时候都可能被挂起,以便另外一个线程可以运行)。
    (5)3个基本状态:就绪,执行,阻塞
    (6)一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程,可以有多个线程)。
    (7)线程是进程的基本执行的单元,一个进程(程序)的所有的任务都在线程中执行。
    (8)一个线程中的任务的执行是串行的。(如果要在一个线程中执行多个任务,那么只能一个一个地按照顺序执行这些任务)。
    (9)在同一时间内,一个线程只能执行一个任务。
    

    多线程

    1、简介
     (1)一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。
     (2)多线程技术可以提高程序的执行效率。
     
    2、原理
     (1)同一时间,CPU只能处理一条线程,只有一条线程在工作(执行)。
     (2)多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。
     (3)如果CPU调度线程的时间足够快,就会造成了多个线程并发执行的假象。
     (4)如果线程非常多,CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源,每条线程被调用执行的频次会降低(线程的执行效率降低)。
     
    3、优缺点
    优点:
        适当提高程序的执行效率、资源利用率(CPU、内存利用率)。
     (1)对于单线程的应用而言,整个应用只有一个顺序执行流,当执行流在某个耗时操作或不能立即完成的任务时(如网络请求、复杂的操作和运行过程、某些必须消耗一定时间后才能完成的操作),该执行流就会被阻塞,整个应用就会被卡在那里无法继续执行,因此单线程的程序往往功能非常有限。
     (2)在实际应用中多线程是非常有用的,例如:用户操作界面上的某个按钮时,该按钮需要执行的任务需要几秒甚至十几秒,如果直接在UI线程(主线程)中完成这个任务,在该任务执行完成、返回之间,UI线程被该任务阻塞,无法响应用户的其他操作(这段时间内,用户对iOS系统的操作将不会有任何反应,除非用户单机Home键返回,但这并不是用户希望的结果)。
    缺点:
     (1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
     (2)线程越多,CPU在调度线程上的开销就越大。
     (3)程序设计更加复杂:如线程之间的通信,多线程的数据共享。
     
    4、在iOS开发中的应用
        一个iOS程序运行后,默认会开启1条线程,称为主线程(UI线程)。
        主线程用来
            1、显示、刷新UI界面
            2、处理UI事件(点击事件、滚动事件、拖曳事件等)。
            3、比较耗时的操作不要放在主线程中。耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种卡的坏的体验。
    
    多线程资源共享的安全隐患
            一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。当多个线程访问同一资源时,很容易引发数据散乱和数据安全问题。(如多个线程访问同一个对象、同一个变量、同一个文件)。
    
    解决方法:使用互斥锁
            使用的格式:@synchronized(锁对象){//需要锁定的代码}     注:锁定一份代码只用一把锁,用多吧锁是无效的。
        互斥锁的优点
            能有效防止因多线程抢夺资源造成的数据安全问题
        互斥锁的缺点
            需要消耗大量的CPU资源。
        互斥锁的使用前提
            多条线程抢夺同一块资源。(互斥锁就是使用了线程同步技术,多条线程按顺序地执行任务)。
    
    示例:在创建单例模式时,最好使用互斥锁。
     static UserInfo *singletonUserInfo = nil;
     +(UserInfo *)shareUserInfo{
     @synchronized(self){
     //创建一个线程锁,防止多线程中多次创建
     if (singletonUserInfo == nil) {
     singletonUserInfo = [[UserInfo alloc]init];
     NSLog(@"初始化");
     }
     }
     return singletonUserInfo;
     }
    
    
    atomic
        原子性,为setter方法加锁(默认就是atomic),线程安全,需要消耗大量的资源
    nonatomic
        非原子性,不会为setter方法加锁,非线程安全,适合内存小的移动设备
     iOS开发:最好所有属性都声明为nonatomic,尽量避免多线程抢夺同一块资源,尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
    

    简单总结

     1.一个程序至少有一个进程,一个进程至少有一个线程。
        进程创建则主线程创建。
        主线程死掉则进程死掉。
     2.进程是最小的资源分配单位,线程是最小的CPU执行单位。
        进程之间相互独立,且所有任务都交由线程去执行。
        多个线程之间相互独立,但共享同一进程的资源。
     3.更新UI要放在主线程中(否则:防止造成卡顿,避免多个子线程同时操作UI)
    
    同步/异步/串行/并行
    
     同步:任务块完成后再继续向下执行
     异步:跳过任务块继续向下执行
     串行:是指一个队列,队列中的任务依次执行
     并行:是指一个队列,队列中的任务同时执行
     
     4种(首先先考虑队列,再考虑同异步)
       同步串行(死锁)
       同步并行
       异步串行
       异步并行
    

    2.2 使用

    NSThread

    创建线程
    (方式一)(创建后调用start启动)
        NSThread *thread=[[NSThread alloc]initWithTarget:self selector:@selector(method:) object:@""];
    (方式二)(创建后调用start启动)
        NSThread *thread1=[[NSThread alloc]initWithBlock:^{
        }];
    (方式三)(创建后自动启动)
        [NSThread detachNewThreadSelector:@selector(method:) toTarget:self withObject:@""];
    (方式四)(创建后自动启动)
        [NSThread detachNewThreadWithBlock:^{
        }];
    (方式五)(隐式创建)
        //
        [self performSelector:@selector(method)];
        [self performSelector:@selector(method) withObject:nil];
        [self performSelector:@selector(method) withObject:nil withObject:nil];
        [self performSelector:@selector(method) withObject:nil afterDelay:1.0];
        [self performSelector:@selector(method) withObject:nil afterDelay:1.0 inModes:@[NSRunLoopCommonModes]];
        //
        [self performSelector:@selector(method) onThread:thread withObject:nil waitUntilDone:true];
        [self performSelector:@selector(method) onThread:thread withObject:nil waitUntilDone:true modes:@[NSRunLoopCommonModes]];
        //
        [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:true];
        [self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:true modes:@[NSRunLoopCommonModes]];
        //
        [self performSelectorInBackground:@selector(method) withObject:nil];
    
    thread实例方法
    
        // 是否是主线程
        BOOL isMain=[thread isMainThread];
        // 是否被取消
        BOOL isCanceled=[thread isCancelled];
        // 是否正在执行
        BOOL isExecuting=[thread isExecuting];
        // 是否已结束
        BOOL isFinished=[thread isFinished];
        // 是否是主线程
        BOOL isMainThread=[thread isMainThread];
    
        // 获取线程名
        NSString *threadName=thread.name;
        // 设置线程名
        [thread setName:@"name"];
        // start
        [thread start];
        // cancel
        [thread cancel];
    
        // 获取所占的栈区大小
        NSUInteger *size=[thread stackSize];
        // 设置所占的栈区大小
        [thread setStackSize:1024*5];
    
        // 获取优先级
        double x=thread.threadPriority;
        // 设置优先级
        [thread setThreadPriority:1000];
    
    NSThread类方法
    
        // 获取当前线程
        NSThread *currentThread=[NSThread currentThread];
        // 杀死当前线程
        [NSThread exit];
        // 阻塞当前线程10s
        [NSThread sleepForTimeInterval:10];
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
        // 判断当前线程是否多线程
        BOOL isMutiThread=[NSThread isMultiThreaded];
    
        // 获取主线程
        NSThread *mainThread=[NSThread mainThread];
        // 判断当前线程是否是主线程
        BOOL isMainThread=[NSThread isMainThread];
    
        // 获取当前线程的调度优先级
        // 调度优先级的取值范围是0.0~1.0,默认0.5,值越大,优先级越高。
        double priority=[NSThread threadPriority];
        // 设置调度优先级
        [NSThread setThreadPriority:0.5];
    
        // 即将进入多线程模式
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(willBecomeMuti:) name:NSWillBecomeMultiThreadedNotification object:nil];
        // 进入单线程模式
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(becomeSigle:) name:NSDidBecomeSingleThreadedNotification object:nil];
        // 线程退出
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(willExit:) name:NSThreadWillExitNotification object:nil];
    

    GCD

    同步/异步/主线程/全局线程
        // 同步  1.主线程(串行)
        dispatch_sync(dispatch_get_main_queue(), ^{
        });
        // 异步  2.全局线程(并行)
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
        });
    自定义线程队列    
        // 串行
        dispatch_queue_t queue=dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        // 并行
        dispatch_queue_t queue2=dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    
    
    延迟
        // 延迟2s执行
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*2), dispatch_get_main_queue(), ^{
        });
        
    重复
        // 重复3次执行
        dispatch_apply(3, dispatch_get_main_queue(), ^(size_t x) {
        });
        
    onlyOne    
        // 仅执行一次
        static dispatch_once_t predicate;
        dispatch_once(&predicate, ^{
            //
        });
        
    
    组    dispatch_group_t
        //
        dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_t group=dispatch_group_create();
        dispatch_group_async(group, dispatchQueue, ^{
        });
        // group内所有队列完成后调用
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        });
    
    
    同步锁
        // dispatch_semaphore_t 信号量:用于同步
        // 1.创建信号量(个数>=0)  (放在代码块上)
        dispatch_semaphore_t semaphore=dispatch_semaphore_create(0);
        // 2.信号量个数+1          (放在代码块中)
        dispatch_semaphore_signal(semaphore);
        // 3. 等待直到信号量个数>0   (放在代码块下)
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        // 3. 10s内等待直到信号量个数>0
        dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10));
    
        
    同步锁
        // dispatch_group_t同步
        dispatch_group_t group=dispatch_group_create();
        // 进入组
        dispatch_group_enter(group);
        dispatch_group_async(group, dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT), ^{
            // 离开组
            dispatch_group_leave(group);
        });
        // 等待直到离开组
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    
    栅栏
        // dispatch_barrier_async先并发执行栅栏之前任务,在执行栅栏任务,在并发执行栅栏后的任务
        dispatch_queue_t concurrentQueue = dispatch_queue_create("my.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-1");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-2");
        });
        dispatch_barrier_async(concurrentQueue, ^(){
            NSLog(@"dispatch-barrier");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-3");
        });
        dispatch_async(concurrentQueue, ^(){
            NSLog(@"dispatch-4");
        });
    

    NSOperation

    NSOperation是对GCD的封装,相比GCD,NSOperation能处理大量的并发请求,更好的处理队列之间的依赖关系,缺点是比GCD稍慢效率低.
    
        // 1.创建queue队列
        NSOperationQueue *optionQueue=[NSOperationQueue new];
        // 1.1设置queue最大并行数
        [optionQueue setMaxConcurrentOperationCount:5];
        
        // 2.添加blockOption到queue
        // 创建option 
        NSBlockOperation *option=[NSBlockOperation blockOperationWithBlock:^{
        }];
        // 设置option代码块完成时调用
        [option setCompletionBlock:^{
        }];
        // 添加option到queue
        [optionQueue addOperation:option];
    
        // 2.1 option添加依赖(option2完成后才能执行option1)
        NSBlockOperation *option2=[NSBlockOperation blockOperationWithBlock:^{
        }];
        [option addDependency:option2];
        // 删除依赖
        [option removeDependency:option2];
    
        // 2.添加blockOption到queue(匿名)
        [optionQueue addOperationWithBlock:^{
        }];  
    
    
        // 2. 添加InvocationOperation到queue
        NSInvocationOperation *option=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(method) object:nil];
        // 添加option到queue
        [optionQueue addOperation:option];
    
    
        // 2. 添加option数组
        [optionQueue addOperations:@[] waitUntilFinished:true];
    
    queue 队列方法
    
        // 获取 option数量(readOnly)
        NSUInteger operationCount=optionQueue.operationCount;
        // 获取 最大option数量
        NSInteger maxConcurrentOperationCount=optionQueue.maxConcurrentOperationCount;
    
        // 设置/获取 name
        [optionQueue setName:@""];
        NSString *name=optionQueue.name;            
    
        // queue是否暂停
        BOOL isSuspended=[optionQueue isSuspended];
        // queue暂停
        [optionQueue setSuspended:true];
    
        // queue取消所有option
        [optionQueue cancelAllOperations];
        // 阻塞直到queue所有option完成
        [optionQueue waitUntilAllOperationsAreFinished];
    
    option(NSOperation) 方法
    
        // option设置name
        [option setName:@""];
        // option 开始
        [option start];
        // option 取消
        [option cancel];
    
        // 阻塞直到执行完毕
        [option waitUntilFinished];
    
        // 获取优先级
        NSOperationQueuePriority queuePriority=option.queuePriority;
        // 设置优先级
        [option setQueuePriority:NSOperationQueuePriorityHigh];
        /*
        NSOperationQueuePriorityVeryLow = -8L,
        NSOperationQueuePriorityLow = -4L,
        NSOperationQueuePriorityNormal = 0,
        NSOperationQueuePriorityHigh = 4,
        NSOperationQueuePriorityVeryHigh = 8
        */
    
        // option 是否取消
        BOOL isCanceled=[option isCancelled];
        // option 是否正在执行
        BOOL isExecuting=[option isExecuting];
        // option 是否结束
        BOOL isFinished=[option isFinished];
        // option 是否异步
        BOOL isAsy=[option isAsynchronous];
        // 是否就绪
        BOOL isReady=[option isReady];
    
        // 获取依赖列表
        NSArray<NSOperation *> *dependencies=option.dependencies;
    
    自定义NSOperation
    
    #import <Foundation/Foundation.h>
    @protocol YTOperationCompletionDelegate  
    // 更新UI
    -(void)opCompletion:(NSString *)str;    // 参数看实际情况变化
    @end
    @interface YTMyOperation : NSOperation
    @property (nonatomic,weak) id<YTOperationCompletionDelegate> dele;
    @end
    
    
    #import "YTMyOperation.h"
    @implementation YTMyOperation
    -(void)main{
        //
        [super main];
        
        // 一些操作
        //...
        //
        dispatch_async(dispatch_get_main_queue(), ^{
            if(self.dele){
                [self.dele opCompletion:@"complete"];
            }
        });
    }
    @end
    
    blockOption(NSBlockOperation : NSOperation) 方法
    
        // 添加
        [option addExecutionBlock:^{
        }];
    
    InvocationOperation(NSInvocationOperation : NSOperation) 方法
    
    3. block
    block(即闭包,基于函数指针) 可用于传递数据
    
    动画和gcd都用到:
        [UIView animateWithDuration:3 animations:^{     
        }];
        dispatch_async(dispatch_get_main_queue(),^{         
        });
    
    1. 声明(牢记)
    @property (nonatomic,copy) void (^myBLock)(NSString *name);
    
    1. 方法
    // 把声明中的block名字放到外面  就是类型
    -(void)setMyBLock:(void (^)(NSString *))myBLock{  
    }
    -(void (^)(NSString *))getMyBLock{  
    }
    
    1. 调用方法
    // ^(参数类型 参名){}
    [self setMyBLock:^(NSString *name) {    
        // return 10; 又返回值时+
    }];
    myBlock(@"name");
    
    1. 局部变量
    // 局部变量
    void (^myBlock)(NSString *name)=^(NSString *name){
    };
    
    1. 重定义(起别名)
        typedef void (^MyBlock) (NSString *name);
        MyBlock myBlock=^(NSString *name){
        
        };
    
    4. KVC
        KVC键值编码(k:键  V:值  C:编码)
        是一种不通过存取方法而通过属性名间接访问属性的方式。
    
    @interface NSObject(NSKeyValueCoding)
    定义了KVC相关方法
    @end
    
    
    用于:
        1.
        2.
    
    使用
        PersonModel *personM=[PersonModel new];
        
        // 首先 查找set方法->若没有则查找变量->若还没有则调用setValueforUndefinedKey->若没实现setValueforUndefinedKey则蹦
        [personM setValue:@"张三丰" forKey:@"name"];
        // 首先 查找get方法->若没有则查找变量->若还没有则调用valueforUndefinedKey->若没实现valueforUndefinedKey则蹦
        NSString *name=[personM valueForKey:@"name"];
    
    
        // 多级路径
        [personM setValue:@"" forKeyPath:@"dog.name"];
        int dogAge=[personM valueForKeyPath:@"dog.age"];
    

    PersonModel.h

    #import <Foundation/Foundation.h>
    @interface PersonModel : NSObject
    @property (nonatomic,copy) NSString *name;
    @end
    

    PersonModel.m

    #import "PersonModel.h"
    @implementation PersonModel
    -(instancetype)initWithDic:(NSDictionary *)dic{
        self=[super init];
        if(self){
            // 给模型所有属性赋值(等价于循环setValueForKey给属性赋值)
            [self setValuesForKeysWithDictionary:dic];
        }
        return self;
    }
    // 找不到键时调用
    -(void)setValue:(id)value forUndefinedKey:(NSString *)key{}
    
    // 覆写以下方法 做额外操作
    -(void)setValue:(id)value forKey:(NSString *)key{
        [super setValue:value forKey:key];
    }
    -(void)setValue:(id)value forKeyPath:(NSString *)keyPath{
        [super setValue:value forKeyPath:keyPath];
    }
    -(void)setValuesForKeysWithDictionary:(NSDictionary<NSString *,id> *)keyedValues{
        [super setValuesForKeysWithDictionary:keyedValues];
    }
    @end
    
    5. KVO
        KVO键值观察(k:键  V:值  O:观察)
        监测某属性的变化(观察者模式的衍生)
    
        PersonModel *personM=[PersonModel new];
        /*
         options :会观察到并传入observeValueForKeyPath方法
            NSKeyValueObservingOptionOld     旧值
           NSKeyValueObservingOptionNew     新值
           NSKeyValueObservingOptionInitial 初始化
           NSKeyValueObservingOptionPrior   分2次调用(在值改变之前和值改变之后)
         */
        // 给personM添加观察者self,观察name属性的变化
        [personM addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
        // 移除观察者self
        [personM removeObserver:self forKeyPath:@"name"];
    
    // 观察方法(当所观察的东西发生变化时调用)
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
        //NSKeyValueChangeNewKey  NSKeyValueChangeOldKey
        
    }
    
    6. 内存管理机制

    管理原则

        OC基于引用计数管理对象的生命周期,每一个对象都绑定一个引用计数器。当通过alloc、new、copy创建对象时,引用计数为1,retain引用计数加1,release引用计数减1,当引用计数为0时则进行销毁(此时会调用delloc)   。
        基本遵循:谁创建谁释放,谁引用谁管理。
    

    管理方式(3种)

    MRC手动引用计数
    
        由程序员手动管理内存,当一个对象被引用时retain(如加入数组(由数组自动+1 -1),成为另一个对象的属性),当一个对象释放时release(移出数组,另一个引用该对象的对象被销毁).
    
    ARC 自动引用计数
    
        由编译器通过对代码的静态分析,在适当的地方添加retain和release(如在delloc、作用范围末尾、加入数组后、set方法中)
        不能调用release retain retainCount autorelease,可以@autoreleasepool{}
        
    ARC下采用非ARC编译某文件(混编):
        需在BuildPharse | Compile File找到相应文件添加 -fobjc-arc 相反+ -fno-objc-arc)
    
    
        __strong        __weak   
        __weak typeOf(self) _weakSelf=self;
        __strong typeOf(self) _strongSelf=_weakSelf;
    
    AutoReleasePool 自动释放池  @autoreleasepool{}
    
        当一个对象调用autorelease时,会将该对象放入自动释放池,在自动释放池结束时向所有自动释放池中的对象发送release进行销毁.
        系统本身提供一个自动释放池,可以通过@autoreleasepool{}创建释放池.
        方法返回对象时必须autorelease
    
    7. Category
    cmd+N 选Objective-C file新建类别文件
    
    例:
    文件NSArray+YTCusArray.h
    #import <Foundation/Foundation.h>
    @interface NSArray (YTCusArray)
    @end
    
    文件NSArray+YTCusArray.m
    #import "NSArray+YTCusArray.h"
    @implementation NSArray (YTCusArray)
    @end
    
    category的作用:
        1.可以在不改变且不知道类的代码的情况下给类添加新方法。
        2.可以将类的实现分散到不同的文件或框架中便于维护.
        3.创建对私有方法的引用,实现非正式协议(即可选方法)。
    
        不允许添加属性(可以通过运行时库实现添加属性)
        可覆写原类中的方法。
        多个类别覆写同一方法,则在Build Phases|Compile Sources中谁在下方谁优先级大
        扩展可以添加属性,声明的方法必须实现(类别则可不实现)
    
    8. 计时器
        三种常见的定时器:NSTimer、CADisplayLink以及GCD Timer
    

    NSTimer

    方式一:
        // 创建计时器(1s后执行)
        NSTimer * timer = [[NSTimer alloc]initWithFireDate:[NSDate distantPast] interval:1 target:self selector:@selector(myLog:) userInfo:nil repeats:YES];
        // 添加
        [[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
        /*
    消息处理机制(不断循环检测事件发生)
         NSDefaultRunLoopMode   标准优先级(默认)优先级低。在滚动时NSTimer会失效,可添加到NSRunLoopCommonModes中。
         NSEventTrackingRunLoopMode 则仅在滚动时有效(用于SV和别的控件的动画)
         NSRunLoopCommonModes(二者合一)优先级高
         */
        // 激活计时器
        [timer fire];
        
    
    // 创建并激活计时器
        NSTimer *timer2=[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:true block:^(NSTimer * _Nonnull timer) {
        }];
    // 创建并激活计时器
        NSTimer *timer3=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(handleTimer) userInfo:nil repeats:true];
    
    
    
    
        // 暂停计时器
        timer.fireDate=[NSDate distantPast];
        // 继续计时器
        timer.fireDate=[NSDate date];
        // 销毁计时器
        [timer invalidate];
        timer=nil;
    
    方式二:NSInvocation
        // 1.初始化一个Invocation对象
        //
        NSInvocation * invo = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:@selector(handleTimer:)]];
        // 或
        NSInvocation *invo=[NSInvocation new];
        [invo setTarget:self];
        [invo setSelector:@selector(handleTimer:)];
        
        // 2.创建NSTimer
        // 创建NSTimer(1s,invo,是否重复)
        NSTimer * timer = [NSTimer timerWithTimeInterval:1 invocation:invo repeats:YES];
        // 加入主循环池中
        [[NSRunLoop mainRunLoop]addTimer:timer forMode:NSDefaultRunLoopMode];
        // 启动(开始循环)
        [timer fire];
        或
        // 创建NSTimer并启动
        NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 invocation:invo repeats:YES];
    
    9. protocol协议
    protocol协议
        只有方法声明,没有方法实现。
        遵循协议(则拥有方法的声明),就需要实现必须实现的方法(@required 修饰)。
            @protocol
            @required   // 默认
            // 必须实现的方法...
            @optional
            // 可选实现的方法...
            @end
        
    
    10. 架构模式
    常用
    
      MVC
      MVVM
    
        MVC模式    
            M   表示Model模型层,数据模型(用来存储和传递数据)  
            V    表示View视图层,界面UI(用来显示数据)
            C    表示Controller控制层,UIController(用来将数据显示到视图上,处理用户交互) 
                    Model和View层不能直接通信
    
        MVVM模式    
            M   表示Model模型层,数据模型(用来存储和传递数据)  
            V    表示View视图层,界面UI(用来显示数据)
            VM 表示模型数据处理层,用来请求网络获取数据,并转换为UI所需的数据格式(简化C)
            C    表示Controller控制层,UIController(用来将数据显示到视图上,处理用户交互) 
    

    相关文章

      网友评论

        本文标题:OC基础快速恢复2

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