iOS 協議(protocol) 和通知(NSNotification)一般人不知道的點

語言: CN / TW / HK

這是我參與11月更文挑戰的第17天,活動詳情檢視:2021最後一次更文挑戰

協議(protocol)

關於協議的一些定義,簡單使用方法就不多說了.

協議新增新增屬性

一般人可能不知道,協議除了可以新增方法以外,還可以新增屬性,當一個物件遵守了協議後,他就也擁有了協議中的屬性

需要注意的是,協議中的屬性是沒有getter/setter方法的,需要我們使用關鍵字@synthesize宣告需要系統合成getter/setter方法。

關於協議新增屬性來舉個例子: ```

import

@protocol PersonProtocol @property (nonatomic, strong) NSString *name;//協議中的屬性 @end

@interface Person : NSObject @property (nonatomic, weak) id delegade; @end ```

找個類實現協議 ```

import "Student.h"

@interface Student () @end

@implementation Student //協議裡的屬性 @synthesize name;

@end

應用以下: Person onePerson = [Person new]; Student xiaoming = [Student new]; onePerson.delegade = xiaoming; onePerson.delegade.name = @"xiaoming"; NSLog(@"%@",xiaoming.name); ``` 列印結果如下

xiaoming

通知(NSNotification)

先來看下常用的發通知的套路 傳送: -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"要發通知啦"); [[NSNotificationCenter defaultCenter] postNotificationName:@"aaa" object:nil]; NSLog(@"通知發完啦"); } 接收: ``` - (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(testNoti) name:@"aaa" object:nil];
return YES;

}

  • (void)testNoti { NSLog(@"收到通知啦 %@",[NSThread currentThread]); } ```

看下列印: 要發通知啦 收到通知啦 <NSThread: 0x17406d880>{number = 1, name = main} 通知發完啦 總結:

通過postNotificationName:傳送通知是同步的, 會卡住當前執行緒. 就是說, 傳送通知後,會等到對方接收到通知並執行完收到通知的方法後才會接著往下走.

關於移除通知

iOS 9以後UIViewController 新增通知後可以不用移除, 系統會自動給移除.

非同步傳送通知

非同步傳送通知需要三步 1. 建立通知(NSNotification) 2. 建立通知佇列(NSNotificationQueue) 3. 將通知放入佇列中執行

傳送: NSLog(@"傳送通知前"); //建立通知 NSNotification *noti = [[NSNotification alloc] initWithName:@"aaa" object:nil userInfo:nil]; //將通知加入佇列,並設定為NSPostASAP,噹噹前runloop完成之後立即post [[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostASAP]; NSLog(@"傳送通知後");

接收: ``` - (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(testNoti) name:@"aaa" object:nil];
return YES;

}

  • (void)testNoti { NSLog(@"接收到通知了%@",[NSThread currentThread]); } ```

列印: ``` 傳送通知前 傳送通知後 接收到通知了{number = 1, name = main}

```

將通知加入到佇列的方法有個列舉引數: typedef NS_ENUM(NSUInteger, NSPostingStyle) { NSPostWhenIdle = 1, // 當runloop處於空閒狀態時post NSPostASAP = 2, // 噹噹前runloop完成之後立即post NSPostNow = 3 // 立即post,同步(會立即執行) }; 指定列舉引數後, 佇列會根據type, 在合適的時機將NSNotification 傳送到NSNotificationCenter,這就達到了非同步執行的效果.

NSPostWhenIdleNSPostASAP差不多, 都會非同步執行,NSPostNow會立即執行

通知合併

將通知放入佇列執行還有另外一個方法: - (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes;

方法的第二個引數:(NSNotificationCoalescing)coalesceMask就是控制通知的合併策略.

如下: typedef NS_OPTIONS(NSUInteger, NSNotificationCoalescing) { NSNotificationNoCoalescing = 0, // 不合成 NSNotificationCoalescingOnName = 1, // 根據NSNotification的name欄位進行合成 NSNotificationCoalescingOnSender = 2 // 根據NSNotification的object欄位進行合成 };

傳送: - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"傳送通知前"); NSNotification *noti = [[NSNotification alloc] initWithName:@"aaa" object:nil userInfo:nil]; [[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostASAP]; [[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostWhenIdle]; [[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:nil]; NSLog(@"傳送通知後"); }

接收: ``` - (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(testNoti) name:@"aaa" object:nil];
return YES;

}

  • (void)testNoti { NSLog(@"接收到通知了%@",[NSThread currentThread]); } ```

列印: 傳送通知前 傳送通知後 接收到通知了<NSThread: 0x280fbee40>{number = 1, name = main}

可以看到發了三個通知,最後卻收到了一條,說明,通知進行和合並。

注意, 如果直接使用postNotificationName:或者指定NSPostNow提交方式或者指定合併策略為NSNotificationNoCoalescing 時,不會合並通知