一,单例模式:通过static关键词,声明全局变量。在整个进程运行期间只会被赋值一次。

/**
 static : 修饰变量
 1> 修饰全局变量
 * 全局变量的作用域仅限于当前文件内部(不加的话别人使用extern关键字就能从其他文件访问这个文件的全局变量了)
 2> 修饰局部变量 :
  * 能保证局部变量永远只初始化1次,在程序运行过程中,永远只有1分内存
 * 局部变量的生命周期跟全局变量类似
 * 但是不能改变作用域
 */

#pragma mark 懒汉式单例 arc
#import "HMMusicTool.h"
@implementation HMMusicTool

static id _instance; //保证这个全局变量只能当前文件可以访问

/**
 *  alloc方法内部会调用这个方法
 */
+ (id)allocWithZone:(struct _NSZone *)zone
{
    if (_instance ==nil) { //防止频繁加锁
        @synchronized(self) {
            if (_instance ==nil) { //防止创建多次
                _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}

+ (instancetype)sharedMusicTool
{
    if (_instance ==nil) { //防止频繁加锁
        @synchronized(self) {
            if (_instance ==nil) { //防止多次init
                _instance = [[self alloc] init];
            }
        }
    }
    return _instance;
}

//copy方法内部会调用这个方法
- (id)copyWithZone:(NSZone *)zone
{
     return _instance;//保证copy方法调用之后也是这一个对象不会产生新的对象,因为copy可能拷贝产生新的对象
}
@end

  饿汉式 (不需要掌握)
//#import "HMSoundTool.h"
//@implementation HMSoundTool
//
//static id _instance;
//
///**
// *  当类加载到OC运行时环境中(内存),就会调用一次(一个类只会加载1次)
// */
//+ (void)load
//{
//    _instance = [[self alloc] init];
//}
//
//+ (id)allocWithZone:(struct _NSZone *)zone
//{
//    if (_instance == nil) { // 防止其他创建多次
//        _instance = [super allocWithZone:zone];
//    }
//    return _instance;
//}
//
//+ (instancetype)sharedSoundTool
//{
//    return _instance;
//}
//
//- (id)copyWithZone:(NSZone *)zone
//{
//    return _instance;
//}
//
/**
 *  当第一次使用这个类的时候才会调用 (如果使用这个类的方法还需要调用父类的这个方法,比如继承,那么弗雷德initialize方法也会调用)和load的区别就在这
 */
+ (void)initialize
{
    NSLog(@"HMSoundTool---initialize");
}
//@end

#pragma mark GCD模式单例  arc

#import <Foundation/Foundation.h>

@interface HMDataTool : NSObject
+ (instancetype)sharedDataTool;
@end

#import "HMDataTool.h"

@implementation HMDataTool
// 用来保存唯一的单例对象
static id _instace;

+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [super allocWithZone:zone];
    });
    return _instace;
}

+ (instancetype)sharedDataTool
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [[self alloc] init];
    });
    return _instace;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _instace;
}
@end

#pragma mark GCD单例模式非arc

#import "HMDataTool.h"

@implementation HMDataTool
// 用来保存唯一的单例对象
static id _instace;

+ (id)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [super allocWithZone:zone];
    });
    return _instace;
}

+ (instancetype)sharedDataTool
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instace = [[self alloc] init];
    });
    return _instace;
}

- (id)copyWithZone:(NSZone *)zone
{
    return _instace;
}

//非arc多了这几个方法
- (onewayvoid)release{
}
- (id)retain {
    returnself;
}
- (NSUInteger)retainCount {
    return1;
}
- (id)autorelease {
    returnself;
}

@end

#pragma mark 宏实现单例,自定义名称,适配arc和非arc
创建一个HMSingleton.h文件,在.h文件中写以下宏
// .h文件
#define HMSingletonH(name) + (instancetype)shared##name;

// .m文件
#if __has_feature(objc_arc)

#define HMSingletonM(name) \
static id _instace; \
\
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##name \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [[self alloc] init]; \
}); \
return _instace; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instace; \
}

#else

#define HMSingletonM(name) \
static id _instace; \
\
+ (id)allocWithZone:(struct _NSZone *)zone \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [super allocWithZone:zone]; \
}); \
return _instace; \
} \
\
+ (instancetype)shared##name \
{ \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
_instace = [[self alloc] init]; \
}); \
return _instace; \
} \
\
- (id)copyWithZone:(NSZone *)zone \
{ \
return _instace; \
} \
\
- (oneway void)release { } \
- (id)retain { return self; } \
- (NSUInteger)retainCount { return 1;} \
- (id)autorelease { return self;}

#endif

再把HMSingleton.h文件导入pch文件即可
使用的时候就在需要使用的类的.h文件中加一句HMSingletonH .m文件加一句HMSingleton.m即可

二,观察者模式:KVO是典型的通知模式,观察某个属性的状态,状态发生变化时通知观察者。

//1.KVC
#pragma mark -KVC
- (void)viewDidLoad
{
    [super viewDidLoad];
    XLCustom * custom = [[XLCustom alloc]init];
    //<1>
     //原始方法对成员变量进行赋值使用的是setter方法或者点语法
     //原始方法对成员变量进行获取使用的是getter方法或者点语法
    //    custom.name = @"xuli";
    //    [custom setAge:19];
    //    NSLog(@"%@,%d",[custom name],custom.age);
     //[结论]原始方法对成员变量进行赋值和获取前提是该成员变量必须具有setter、getter方法的声明和实现部分
    //<2>KVC
    //KVC 是key-value-coder的缩写 键值编码的简称
     //KVC -------- 对象的属性或者成员变量进行赋值进行赋值(私有的也可以修改),一般用来替换系统的私有的东西
    /*
     1、先去类中查找是否具有该变量的setter方法的声明和实现部分 如果具有直接调用setter方法对成员变量进行赋值
     2、如果不具有 继续查找是否具有以该变量命名的成员变量 如果具有直接赋值
     3、如果不具有 继续查找是否具有以下划线开头以变量命名的成员变量 如果具有直接赋值 如果不具有崩溃
     */
    //KVC 对成员变量进行赋值使用的方法是setValue:forkey(path): 前提是找到赋值的类的对象的指针
    [custom setValue:@"xuli" forKey:@"name"];
    [custom setValue:@(19) forKey:@"age"];
    //KVC 对成员变量进行获取使用的方法的是valueForkey(path):
    NSLog(@"%@,%d",[custom valueForKey:@"name"],[[custom valueForKey:@"age"] intValue]);
    //1.KeyPath和Key的区别
    Person *p = [[Person alloc] init];
    p.dog = [[Dog alloc] init];
    p.dog.bone = [[Bone alloc] init];
    //        p.dog.bone.type = @"狗骨";
    
    //        [p setValue:@"猪骨" forKeyPath:@"dog.bone.type"];
    //        [p.dog setValue:@"猪骨" forKeyPath:@"bone.type"];
    [p.dog.bone setValue:@"猪骨" forKeyPath:@"type"];
    
    NSLog(@"%@", p.dog.bone.type);
    
    //        p.dog.name = @"wangwang";
    //        [p.dog setValue:@"wangcai" forKey:@"name"];
    //        [p.dog setValue:@"larry" forKeyPath:@"name"];
    
    //        [p setValue:@"hashiqi" forKeyPath:@"dog.name"];
    
    //forKeyPath包含了forKey的功能,以后使用forKeyPath就可以了
     //forKeyPath中可以利用.运算符, 就可以一层一层往下查找对象的属性
    //        [p setValue:@"hashiqi" forKey:@"dog.name"]; // 写法错误
    
    //        NSLog(@"%@", p.dog.name)
    //2.关于KVC中的数据数组 (不常用,知道就好)
    Person *p = [[Person alloc] init];
    Book *book1 = [[Book alloc] init];
    book1.name = @"5分钟突破iOS开发";
    book1.price = 10.5;
    Book *book2 = [[Book alloc] init];
    book2.name = @"5分钟突破android开发";
    book2.price = 18.5;
    Book *book3 = [[Book alloc] init];
    book3.name = @"5分钟突破前端开发";
    book3.price = 20.5;
    Book *book4 = [[Book alloc] init];
    book4.name = @"5分钟突破PHP开发";
    book4.price = 10.5;
    p.books = @[book1, book2, book3, book4];
    
     // 获得所有的书名(将所有的书名放到一个数组中)
    //        NSMutableArray *names = [NSMutableArray array];
    //        for (Book *book in p.books) {
    //            [names addObject:book.name];
    //        }
    
     // 取出books数组中每一个元素的name属性值,放到一个新的数组中返回
    NSArray *names = [p valueForKeyPath:@"books.name"];
    [p valueForKeyPath:@"dog.name"];
    NSLog(@"%@", names);
     //取出数组中的books的价格,再把每一个价格加起来 返回值是NSNumber类型的数据
    //NSNumber *avgNumber = [p valueForKeyPath:@"books.@avg.price"]; 求平均值
    NSNumber *sumNumber = [p valueForKeyPath:@"books.@sum.price"];//求和
    NSLog(@"%@", sumNumber);
    //        NSLog(@"%f", [sumNumber doubleValue]);

}

// 2. 使用KVC进行正向和反向传值
//正向传值:给下一个界面设一个属性来接受传过去的值,在这个界面获取下一个界面的指针,用指针[next setValue:[UIColor magentaColor] forKey:@"nextColor"];
//反向传值:在上一个界面设置一个属性接受传过来得值,在下一个界面设一个属性id delegate; 设置下一个界面的属性是上一个界面的指针.然后用代理(即上一个界面的指针)设置上一个界面的属性值,这样就反向传过去了
// 3. KVO 的使用
#pragma mark - KVO
#import "XLViewController.h"
#import "XLBoy.h"
#import "XLGirl.h"
@interface XLViewController ()
{
    XLBoy * boy;
    XLGirl * girl;
}
@end

@implementation XLViewController

-(void)createUI
{
    NSArray * array = @[@"