KVO,KVC,nitification

1.KVO

KVO即键值监听。KVO模式在广泛应用的MVC模式中应用很广泛。在C中注册C为M中属性的监听者,当M中的属性发生改变时在C中产生回调,在回调方法中更新视图V。

  • KVO的使用步骤
    1 注册监听者:
//第一个参数 observer:观察者 (这里观察self.kvo对象的属性变化)
//第二个参数 keyPath: 被观察的属性名称(这里观察 self.myKVO 中 num 属性值的改变)
//第三个参数 options: 观察属性的新值、旧值等的一些配置(枚举值,可以根据需要设置,例如这里可以使用两项)
//第四个参数 context: 上下文,可以为 KVO 的回调方法传值(例如设定为一个放置数据的字典)
[self.kvo addObserver:self forKeyPath:@"num" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];

2 被监听的属性发生改变:
被监听的属性只有遵循KVO变更属性的方式,例如经过setter方法或者kvc模式而改变才会执行KVO的回调。如果赋值没有通过setter方法或者kvc,如直接赋值:_name = @"pd”,则不会执行KVO的回调。
3 执行KVO的回调:

//keyPath:属性名称
//object:被观察的对象
//change:变化前后的值都存储在 change 字典中
//context:注册观察者时,context 传过来的值
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
}

demo

在Controller中添加一个label和一个Button。创建一个Model类KVO,KVO类有一个属性是NSInteger类型的num。在Controller中创建KVO的实例对象,并监听KVO实例对象的num属性。在Controller的Button按钮的实现方法中,改变KVO实例的num属性的值,这样在Controller中会产生回调,在该回调中改变label显示的值。

#import "MainViewController.h"
#import "KVO.h"

@interface MainViewController ()

@property (nonatomic, strong)KVO *kvo;

@end

@implementation MainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.kvo = [[KVO alloc] init];
    
    [self.kvo addObserver:self forKeyPath:@"num" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)dealloc{
    
    [self.kvo removeObserver:self forKeyPath:@"num"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    if([keyPath isEqualToString:@"num"]&&object == self.kvo){
        
        self.label.text = [NSString stringWithFormat:@"值为:%@",
                           [change valueForKey:@"new"]];
    }
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/
- (IBAction)buttonClicked:(id)sender {
    
    self.kvo.num = self.kvo.num +1;
}

@end

2.KVC

下面是kvc最为重要的四个方法:

- (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key;          //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

当然NSKeyValueCoding类别中还有其他的一些方法,下面列举一些

+ (BOOL)accessInstanceVariablesDirectly;
//默认返回YES,表示如果没有找到Set<Key>方法的话,会按照_key,_iskey,key,iskey的顺序搜索成员,设置成NO就不这样搜索

- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;
//KVC提供属性值正确性�验证的API,它可以用来检查set的值是否正确、为不正确的值做一个替换值或者拒绝设置新值并返回错误原因。

- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key;
//这是集合操作的API,里面还有一系列这样的API,如果属性是一个NSMutableArray,那么可以用这个方法来返回。

- (nullable id)valueForUndefinedKey:(NSString *)key;
//如果Key不存在,且没有KVC无法搜索到任何和Key有关的字段或者属性,则会调用这个方法,默认是抛出异常。

- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
//和上一个方法一样,但这个方法是设值。

- (void)setNilValueForKey:(NSString *)key;
//如果你在SetValue方法时面给Value传nil,则会调用这个方法

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
//输入一组key,返回该组key对应的Value,再转成字典返回,用于将Model转到字典。

KVC是怎么寻找key的?

当调用setValue: forKey:时,执行逻辑是这样的:

  • 程序优先调用set<Key>:属性值方法,代码通过setter方法完成设置。注意,这里的<key>是指成员变量名,首字母大小写要符合KVC的命名规则,下同
  • 如果没有找到setName:方法,KVC机制会检查+ (BOOL)accessInstanceVariablesDirectly方法有没有返回YES,默认该方法会返回YES,如果你重写了该方法让其返回NO的话,那么在这一步KVC会执行setValue:forUndefinedKey:方法,不过一般开发者不会这么做。所以KVC机制会搜索该类里面有没有名为_<key>的成员变量,无论该变量是在类接口处定义,还是在类实现处定义,也无论用了什么样的访问修饰符,只要存在以_<key>命名的变量,KVC都可以对该成员变量赋值。
  • 如果该类即没有set<key>:方法,也没有_<key>成员变量,KVC机制会搜索_is<Key>成员变量。
  • 和上面一样,如果该类即没有set<Key>:方法,也没有_<key>_is<Key>成员变量,KVC机制再会继续搜索<key>is<Key>的成员变量。再给它们赋值。
    如果上面列出的方法或者成员变量都不存在,系统将会执行该对象的setValue:forUndefinedKey:方法,默认是抛出异常。
  • 如果开发者想让这个类禁用KVC里,那么重写+ (BOOL)accessInstanceVariablesDirectly方法让其返回NO即可,这样的话如果KVC没有找到set<Key>:属性名时,会直接用setValue:forUndefinedKey:方法。
#import "Pig.h"

@interface Pig()
{
    
    NSString *_name;
    NSString *_isName;
    NSString *name;
    NSString *isName;
    NSString *toSetName;
}

//@property (nonatomic, strong)NSString *name;

@end

@implementation Pig

//- (void)setName:(NSString *)name{
//
//    toSetName = name;
//}
//
//- (NSString *)name{
//    
//    return toSetName;
//}

+(BOOL)accessInstanceVariablesDirectly{
    return YES;
}
-(id)valueForUndefinedKey:(NSString *)key{
    NSLog(@"出现异常,该key不存在%@",key);
    return nil;
}
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
    NSLog(@"出现异常,该key不存在%@",key);
}
@end
#import "ViewController.h"
#import "Pig.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    Pig *pig = [[Pig alloc] init];
    
    [pig setValue:@"cute" forKey:@"name"];
    
//    NSLog(@"%@\n,%@\n,%@\n,%@\n", [pig valueForKey:@"_name"], [pig valueForKey:@"name"], [pig valueForKey:@"_isName"], [pig valueForKey:@"isName"]);
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end
  • 我们在使用setValue:forKey时,首先是寻找的setName:(NSString *)name方法,如果在pig类有属性name,那么编译器就会自动合成setName:(NSString *)name方法。而如果没有name属性,则会直接寻找是否有setName:(NSString *)name这个方法。
    如果setName:(NSString *)name这个方法也没有找到,则应该调用+(BOOL)accessInstanceVariablesDirectly这个方法,看这个方法的返回值,如果这个方法的返回值是NO,那么接下来直接执行-(id)valueForUndefinedKey:(NSString *)key方法,否则按照_name,_isName,name,isName这样的顺序寻找有没有这样的成员变量,如果有则直接赋值。

在KVC中使用keyPath

比如说我们刚刚创建的Pig类有一个属性是owner,这个owner类有属性address,phoneNumber,sex,那么我们在获取owner的address属性时就要先利用valueForKey获取owner属性,然后再次利用valueForKey方法获取owner的address属性。实际上并不需要这么繁琐,KVC提供了一个简洁的解决办法,就是keypath:

- (nullable id)valueForKeyPath:(NSString *)keyPath;                  //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;  //通过KeyPath来设值

我们创建一个Person类

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, strong)NSString *address;

@property (nonatomic, strong)NSString *location;

@end

它有address和location这两个属性。然后我们给Pig类一个Person类型的属性owner。@property (nonatomic, strong)Person *owner;,初始化owner的address属性:self.owner = [[Person alloc] init]; self.owner.address = @"华中科技大学";
然后我们利用valueForKeyPath来获取address这个属性值:

Pig *pig = [[Pig alloc] init];
    
    NSString *address = [pig valueForKeyPath:@"owner.address"];
    
    NSLog(@"%@", address);
  • 上面的代码,如果使用的是valueForKey而不是ValueForKeyPath,那么就会按照valueForKey的顺序去查找,显然是查找不到的,而keyPath的分离机制是第一步分离key,以小数点为分隔符,然后进行两次valueForKey的筛选。

KVC处理异常

我们在使用KVC的时候经常会使用错误的key或者是在setValue:forKey时set了一个Nil值,这是不允许的。

  • 对于使用了错误的key,最终的结果就是找不到对应的成员变量。这时候最终就会调用-(id)valueForUndefinedKey:(NSString *)key这个方法,
    如果我们不重写这个方法,那么执行到这儿来就会直接崩溃。因此我们有必要重写这个方法,打印这个key,防止程序因此崩溃。
  • 对于我们在使用setValue:forKey时set了一个Nil值,程序会调用-(void)setNilValueForKey:(NSString *)key并最终崩溃,因此我们要做的也是重写这个方法,防止它因此崩溃。

KVC处理非对象类型数据和自定义对象

  • KVC处理非对象类型的数据:
    在使用valueForKey时,其返回的数据类型一定是id类型,如果属性的真实类型是值类型或者是结构体类型,那么返回的数据会是NSNumber或者是NSValue类型的。然后开发者需要手动转化为原始的类型。但是我们在使用setValue:forKey时,需要把数据封装成对象类型的数据才行。
  • 对于自定义的对象,KVC也会正确的设值和取值,因为传递进去和取出来的都是id类型,所以需要开发者自己担保类型的正确性。

KVC和字典

当对NSDictionary对象使用KVC时,valueForKey:的表现行为和objectForKey:一样。所以使用valueForKeyPath:用来访问多层嵌套的字典是比较方便的。
KVC中有两个专门用于NSDictionary的方法:

- (NSDictionary<NSString *, id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys;
- (void)setValuesForKeysWithDictionary:(NSDictionary<NSString *, id> *)keyedValues;

其中dictionaryWithValuesForKeys:是传入一组key,再返回这组key对应的属性,然后组成字典。
setValuesForKeysWithDictionary:是传入一个字典,修改其中的属性。

Person *person = [[Person alloc] init];
    
    person.country = @"china";
    person.province = @"hubei";
    person.city = @"wuhan";
    
    NSDictionary *dic = [person dictionaryWithValuesForKeys:@[@"country",@"province",@"city"]];
    
    NSLog(@"%@", dic);
    
    [person setValuesForKeysWithDictionary:@{@"country":@"china",@"province":@"henan",@"city":@"zhenzhou"}];
    
    NSLog(@"%@\n,%@\n, %@\n", person.country, person.province, person.city);

结果:

2018-04-10 20:39:11.103646+0800 KVCDemo[20376:984843] {
    city = wuhan;
    country = china;
    province = hubei;
}
2018-04-10 20:39:11.103832+0800 KVCDemo[20376:984843] china
,henan
, zhenzhou

KVC的正确性验证

所谓的正确性验证就是在setValue:forKey的时候,判断这个value能否被设置成这个key的值。这主要是依赖下面这个方法:

- (BOOL)validateValue:(inout id __nullable * __nonnull)ioValue forKey:(NSString *)inKey error:(out NSError **)outError;

我们在调用这个方法的时候,如果没有重写这个方法,那么会直接返回yes,也就是不做判断。

@implementation Address
-(BOOL)validateCountry:(id *)value error:(out NSError * _Nullable __autoreleasing *)outError{  //在implementation里面加这个方法,它会验证是否设了非法的value
    NSString* country = *value;
    country = country.capitalizedString;
    if ([country isEqualToString:@"Japan"]) {
        return NO;                                                                             //如果国家是日本,就返回NO,这里省略了错误提示,
    }
    return YES;
}
@end
NSError* error;
id value = @"japan";
NSString* key = @"country";
BOOL result = [add validateValue:&value forKey:key error:&error]; //如果没有重写-(BOOL)-validate<Key>:error:,默认返回Yes
if (result) {
    NSLog(@"键值匹配");
    [add setValue:value forKey:key];
}
else{
    NSLog(@"键值不匹配"); //不能设为日本,基他国家都行
}
NSString* country = [add valueForKey:@"country"];
NSLog(@"country:%@",country);

KVC是不会主动去调用-(BOOL)validate<Key>:error:这个方法的,因此我们在重写了这个方法后还是要手动调用,如果我们没有重写这个方法,那么当我们在调用这个方法的时候会直接返回yes。

用KVC实现告诫消息传递

当对容器类进行KVC操作时,valueForKey将会被传递给容器中的每个对象,而不是容器本身,结果会被添加进返回的容器中。

NSArray *array =@[@"china", @"japanese", @"conifonia"];
    
    NSArray *carray = [array valueForKey:@"capitalizedString"];
    
    NSLog(@"%@", carray);
    
    NSArray *lengthArray = [array valueForKeyPath:@"capitalizedString.length"];
    
    for(NSNumber *length in lengthArray){
        
        NSLog(@"%ld", length.integerValue);
    }

结果:

2018-04-10 21:09:53.709009+0800 KVCDemo[20699:1003770] (
    China,
    Japanese,
    Conifonia
)
2018-04-10 21:09:53.709172+0800 KVCDemo[20699:1003770] 5
2018-04-10 21:09:53.709269+0800 KVCDemo[20699:1003770] 8
2018-04-10 21:09:53.709383+0800 KVCDemo[20699:1003770] 9

KVC中的函数操作集合

  • 1.简单集合运算符
    简单集合运算符有@avg, @count, @max, @min, @sum这五种
Person *person1 = [[Person alloc] init];
    person1.name = @"xiaoming";
    person1.age = 12;
    
    Person *person2 = [[Person alloc] init];
    person2.name = @"xiaohong";
    person2.age = 23;
    
    Person *person3 = [[Person alloc] init];
    person3.name = @"xiaohua";
    person3.age = 31;
    
    NSArray *array = @[person1, person2, person3];
    
    NSNumber *max = [array valueForKeyPath:@"@max.age"];
    
    NSNumber *min = [array valueForKeyPath:@"@min.age"];
    
    NSNumber *avg = [array valueForKeyPath:@"@avg.age"];
    
    NSNumber *sum = [array valueForKeyPath:@"@sum.age"];
    
    NSNumber *count = [array valueForKeyPath:@"@count"];
    
    NSLog(@"年龄最大的是:%ld",max.integerValue);
    NSLog(@"年龄最小的是:%ld", min.integerValue);
    NSLog(@"平均年龄是:%ld", avg.integerValue);
    NSLog(@"年龄总数是:%ld", sum.integerValue);
    NSLog(@"数量总数是:%ld", count.integerValue);

打印结果:

2018-04-10 21:28:54.404105+0800 KVCDemo[20965:1018867] 年龄最大的是:31
2018-04-10 21:28:54.404216+0800 KVCDemo[20965:1018867] 年龄最小的是12
2018-04-10 21:28:54.404324+0800 KVCDemo[20965:1018867] 平均年龄是22
2018-04-10 21:28:54.404403+0800 KVCDemo[20965:1018867] 年龄总数是66
  • 2 对象运算符
    对象运算符有两个:@distinctUnionOfObjects@unionOfObjects,他们的返回值都是NSArray,区别在于前者返回的是去重后的数据集,后者返回的是全部的数据集。
Person *person1 = [[Person alloc] init];
    person1.name = @"xiaoming";
    person1.age = 12;
    
    Person *person2 = [[Person alloc] init];
    person2.name = @"xiaohong";
    person2.age = 23;
    
    Person *person3 = [[Person alloc] init];
    person3.name = @"xiaohua";
    person3.age = 31;
    
    Person *person4 = [[Person alloc] init];
    person4.name = @"huahua";
    person4.age = 23;
    
    NSArray *array = @[person1, person2, person3, person4];
    
    NSArray *darray = [array valueForKeyPath:@"@distinctUnionOfObjects.age"];
    NSArray *uarray = [array valueForKeyPath:@"@unionOfObjects.age"];
    
    NSLog(@"去重后%@\n, 去重前%@", darray, uarray);

打印结果:

2018-04-10 21:40:27.142712+0800 KVCDemo[21128:1028066] 去重后(
    31,
    23,
    12
)
, 去重前(
    12,
    23,
    31,
    23
)

3.notification

notification即通知,当我们在不同类之间通信时就要用到通知方法。
使用notification,我们能够把消息发送给多个监听该消息的对象,而不需要知道监听该消息对象的任何信息。消息的发送者将消息发送给通知中心,接受消息者也只需要向通知中心注册自己感兴趣的消息即可。这样就降低了消息的发送者和接收者之间的耦合。

NSNotification

发送方将消息以NSNotification的形式发送给通知中心,然后通知中心将消息派发给注册了该消息的接收方。

@property (readonly, copy) NSString *name;
@property (nullable, readonly, retain) id object;
@property (nullable, readonly, copy) NSDictionary *userInfo;
  • name:通知的名字,一般为字符串。
  • object:通知携带的对象,一般为发送消息的独享本身。
  • userInfo:发送方在发送消息的同时想要传递的参数。
    创建一个notification有下列实例方法:
- (instancetype)initWithName:(NSNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo

类方法:

+ (instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject;
+ (instancetype)notificationWithName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

发送通知

发送通知的方法主要有下列几种:

- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSString *)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

注册监听者

注册监听者有下列几个方法:

- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSString *)aName object:(nullable id)anObject;
- (id <NSObject>)addObserverForName:(nullable NSString *)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block;

很多人不明白这里的object指的是什么,在发送通知的时候也会传递一个object参数,一般情况下发送通知的object参数传递的是发送方自己,那么在注册监听者这里,object参数指代的也是发送方的这个object参数,意思就是接收object对象发出的名为name的通知,如果有其它发送方发出同样name的通知,是不会接收到通知的。如果把name和object这两个参数同时置为nil,则会接收所有的通知。这个可以自行测试。

  • 在注册监听者的时候,大家用的最多的是第一种方式。第二种方式对于大家来说比较陌生,这里多了一个参数queue和一个block,block即受到通知时执行的回调,参数queue指定了这个block在哪个线程中执行,如果block传的是nil,则表示这个回调block在发送通知的线程中执行,也即同步执行。

移除监听者

- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSString *)aName object:(nullable id)anObject;

在iOS9以后已经不需要手动移除监听者。

NSNotificationQueue(通知队列)

  • NSNotificationQueue是notification Center的缓冲池。
    如果我们使用普通的- (void)postNotification:(NSNotification *)notification这种方法来发送通知,那么这个通知就会直接发送到notification Center,notification Center则会直接将其发送给注册了该通知的观察者。但是如果我们使用NSNotificationQueue就不一样了,通知不是直接发送给notification Center,而是先发送给NSNotificationQueue,然后由NSNotificationQueue决定在当前runloop结束或者空闲的时候转发给notification Center,再由notification转发给注册的观察者。通过NSNotificationQueue,可以
    合并重复的通知,以便只发送一个通知。
  • NSNotificationQueue遵循FIFO的顺序,当一个通知移动到NSNotificationQueue的最前面,它就被发送给notification Center,然后notification Center再将通知转发给注册了该通知的监听者。
  • 每一个线程都有一个默认的NSNotificationQueue,这个NSNotificationQueue和通知中心联系在一起。当然我们也可以自己创建NSNotificationQueue,可以为一个线程创建多个NSNotificationQueue。
    NSNotificationQueue的核心方法有下列几个:
//类方法返回当前线程的默认的NSNotificationQueue。
defaultQueue
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes;

上面这个方法是使用NSNotificationQueue来发送通知用的。这里面有四个参数。

  • notification是所要发送的通知。
  • postingStyle 这是一个枚举类型的参数。
typedef NS_ENUM(NSUInteger, NSPostingStyle) {
    NSPostWhenIdle = 1,
    NSPostASAP = 2,
    NSPostNow = 3
};

NSPostingStyle即指发送通知的方式,一共有三种方式。

  1. NSPostWhenIdle
    通过字面意思大概可以知道是在空闲时发送。
    简单地说就是当本线程的runloop空闲时即发送通知到通知中心。
  2. NSPostASAP
    ASAP即as soon as possible,就是说尽可能快。
    当当前通知或者timer的回调执行完毕时发送通知到通知中心。
  3. NSPostNow
    多个相同的通知合并之后马上发送。
  • coalesceMask
    coalesceMask即多个通知的合并方式。它也是一个枚举类型。
    有时候会在一段时间内向NSNotificationQueue发送多个通知,有些通知是重复的,我们并不希望这些通知全部发送带通知中心,那么就可以使用这个枚举类型的参数。
typedef NS_OPTIONS(NSUInteger, NSNotificationCoalescing) {
    NSNotificationNoCoalescing = 0,
    NSNotificationCoalescingOnName = 1,
    NSNotificationCoalescingOnSender = 2
};
  1. NSNotificationNoCoalescing
    不管是否重复,不合并。
  2. NSNotificationCoalescingOnName
    按照通知的名字,如果名字重复,则移除重复的。
  3. NSNotificationCoalescingOnSender
    按照发送方,如果多个通知的发送方是一样的,则只保留一个。
  • modes
    这里的mode指定的是当前的runloop的mode,指定mode后,只有当前线程的runloop在这个特定的mode下才能将通知发送到通知中心。

同步与异步发送

  • 同步发送通知
    当我们使用下列这些方法时是使用的同步发送通知,这些也是我们平时常用的发送通知的方法。
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSString *)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSString *)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

同步指的是,当发送方发送通知后,必须要等到所有的监听者完成监听回调,发送方才会接着执行下面的代码。所以如果监听者的回调有大量的计算要处理的话,发送方会一直等待,只有回调全部结束才接着往下执行。

  • 异步发送通知
    当我们使用NSNotificationQueue(通知队列)的下列方法发送通知时是异步发送通知:
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle;
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes;

异步发送通知即只要发送方的通知发送出去了,不管监听方的回调是否执行完毕,反正我就开始执行下面的代码。
但是!!!需要注意的是,当NSPostingStyle的类型是NSPostWhenIdle和NSPostASAP时确实是异步的,而当类型是NSPostNow时则是同步的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,717评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,501评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,311评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,417评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,500评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,538评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,557评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,310评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,759评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,065评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,233评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,909评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,548评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,172评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,420评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,103评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,098评论 2 352

推荐阅读更多精彩内容