【iOS】通知原則

通知実装メカニズムを見ると、通知センターがオブザーバーへの参照をどのように実装するかを理解できます。Apple は Foundation のソース コードをオープンソースにしていないため、特に GNUStep のソース コード実装について言及します。GNUStep のソース コードのアドレスは、GNUStep ソース コード GitHub ダウンロード アドレスで、特定のソース コードを参照できます。

届出の主な流れ

  • 通知グローバル オブジェクトはNCTblという名前の構造体で、これには 3 つの重要なメンバー変数、つまり 2 つのGSIMapTableテーブル、namednameless、および単一リンク リスト が含まれていますwildcard
    • named通知名が渡される通知を格納するテーブルですhash
    • namelessこれは、通知名がなく、メッセージ送信者が含まれるobject通知を格納するテーブルですhash
    • wildcard受信通知名も受信メッセージ送信者も持たない通知を保存するリンク リストです。
  • 通知を登録するたびに、登録された通知はこれら3 つのNCTblタイプに従って対応する構造に配置されますGSIMapTablewildcard
  • 次に、通知を送信する (メッセージを送信する) たびに、まず一致するすべての通知を格納する配列を作成しGSIArray、次のプロセスに従って限定された通知を配列に追加しますGSIArray
    1. 条件に一致するすべての通知を取得しwildcard、配列に追加しますGSIArray
    2. namelessテーブル内の条件に一致する通知を検索し、配列に追加しますGSIArray
    3. namedテーブル内の条件に一致する通知を検索し、配列に追加しますGSIArray
  • 最後に、条件を満たすすべての通知が追加された後、配列全体が走査され、通知メッセージがGSIArray順番に送信されます。performSelector:withObject

届出原則

データ構造

_GSIMapTableマッピング テーブルのデータ構造図は次のとおりです。

関連するデータ構造:

  • _GSIMapTableマッピング テーブルには、、、、、nodeChunksbukets含まれますbuketCountchunkCount
  • nodeChunks:はポインタの配列へのポインタnodeChunksです。GSIMapNodeこれは、ハッシュ テーブル ( ) のノードを格納するために使用される動的に割り当てられたメモリ ブロックを管理するために使用されますGSIMapNode
  • bukets: 単一リンク リストのノード ポインタ配列内の各リンク リストのノード数と先頭アドレスを記録します。
  • bucketCount:nodeノードの数を記録します。
  • chunkCount: 単一リンクされたリスト ノード ポインター配列の数を記録します。
  • nodeCount: ハッシュ テーブルで現在使用されているノードの数。

ソースコードを定義します。

typedef struct _GSIMapBucket GSIMapBucket_t;
typedef struct _GSIMapNode GSIMapNode_t;

typedef GSIMapBucket_t *GSIMapBucket;
typedef GSIMapNode_t *GSIMapNode;

typedef struct _GSIMapTable GSIMapTable_t;
typedef GSIMapTable_t *GSIMapTable;

struct	_GSIMapNode {
    
    
    GSIMapNode	nextInBucket;	/* Linked list of bucket.	*/
    GSIMapKey	key;
#if	GSI_MAP_HAS_VALUE
    GSIMapVal	value;
#endif
};

struct	_GSIMapBucket {
    
    
    uintptr_t	nodeCount;	/* Number of nodes in bucket.	*/
    GSIMapNode	firstNode;	/* The linked list of nodes.	*/
};

struct	_GSIMapTable {
    
    
  NSZone	*zone;
  uintptr_t	nodeCount;	/* Number of used nodes in map.	*/
  uintptr_t	bucketCount;	/* Number of buckets in map.	*/
  GSIMapBucket	buckets;	/* Array of buckets.		*/
  GSIMapNode	freeNodes;	/* List of unused nodes.	*/
  uintptr_t	chunkCount;	/* Number of chunks in array.	*/
  GSIMapNode	*nodeChunks;	/* Chunks of allocated memory.	*/
  uintptr_t	increment;
#ifdef	GSI_MAP_EXTRA
  GSI_MAP_EXTRA	extra;
#endif
};

マッピング テーブルに追加/削除する具体的なコードは次のとおりです。

GS_STATIC_INLINE GSIMapBucket
GSIMapPickBucket(unsigned hash, GSIMapBucket buckets, uintptr_t bucketCount)
{
    
    
    return buckets + hash % bucketCount;
}

GS_STATIC_INLINE GSIMapBucket
GSIMapBucketForKey(GSIMapTable map, GSIMapKey key)
{
    
    
    return GSIMapPickBucket(GSI_MAP_HASH(map, key),
                            map->buckets, map->bucketCount);
}

GS_STATIC_INLINE void
GSIMapLinkNodeIntoBucket(GSIMapBucket bucket, GSIMapNode node)
{
    
    
    node->nextInBucket = bucket->firstNode;
    bucket->firstNode = node;
}

GS_STATIC_INLINE void
GSIMapUnlinkNodeFromBucket(GSIMapBucket bucket, GSIMapNode node)
{
    
    
    if (node == bucket->firstNode)
    {
    
    
        bucket->firstNode = node->nextInBucket;
    }
    else
    {
    
    
        GSIMapNode	tmp = bucket->firstNode;
        
        while (tmp->nextInBucket != node)
        {
    
    
            tmp = tmp->nextInBucket;
        }
        tmp->nextInBucket = node->nextInBucket;
    }
    node->nextInBucket = 0;
}

実際には、これはハッシュ テーブル構造であり、各単一リンク リストの最初の要素を配列の形式で取得し、リンク リスト構造を使用して追加または削除できます。

通知グローバル オブジェクト テーブルの構造は次のとおりです。

typedef struct NCTbl {
    
    
    Observation		*wildcard;	/* Get ALL messages*///获取所有消息
    GSIMapTable		nameless;	/* Get messages for any name.*///获取任何名称的消息
    GSIMapTable		named;		/* Getting named messages only.*///仅获取命名消息
    unsigned		lockCount;	/* Count recursive operations.	*///递归运算计数
    NSRecursiveLock	*_lock;		/* Lock out other threads.	*///锁定其他线程
    Observation		*freeList;
    Observation		**chunks;
    unsigned		numChunks;
    GSIMapTable		cache[CACHESIZE];
    unsigned short	chunkIndex;
    unsigned short	cacheIndex;
} NCTable;

最も重要なデータ構造は、 、、および単一リンク リストの2 つのGSIMapTableテーブルです。namednamelesswildcard

  • named、受信通知の名前を保持する通知hashテーブル
  • nameless、通知名を渡さずに、メッセージobject送信者を渡してhashテーブルを保存します。
  • wildcardobject、通知名も受信通知も含まない、単一リンクされたリストを保持します。

通知名を含む通知テーブルを保存するにはnamed登録されたobjectオブジェクトが必要であるため、テーブル構造は として渡されnameますkeyまた、対応するオブジェクトのオブジェクトを格納するために使用されるテーブルvalueでもありますGSIMapTableobjectobserver

通知名を渡さずobjectオブジェクトのみを渡す通知テーブルの場合は、の対応関係のみnamelessを保存すればよいため、として使用しますobjectobserverobjectkeyobservervalue

オブザーバーを追加する特定のコア関数 (ブロック形式はこの関数の単なるラッパーです) は、大まかに次のようにコード化されます。

- (void) addObserver: (id)observer
            selector: (SEL)selector
                name: (NSString*)name
              object: (id)object
{
    
    
    Observation	*list;
    Observation	*o;
    GSIMapTable	m;
    GSIMapNode	n;

    //入参检查异常处理
    ...
		//table加锁保持数据一致性
    lockNCTable(TABLE);
		//创建Observation对象包装相应的调用函数
    o = obsNew(TABLE, selector, observer);
		//处理存在通知名称的情况
    if (name)
    {
    
    
        //table表中获取相应name的节点
        n = GSIMapNodeForKey(NAMED, (GSIMapKey)(id)name);
        if (n == 0)
        {
    
    
           //未找到相应的节点,则创建内部GSIMapTable表,以name作为key添加到talbe中
          m = mapNew(TABLE);
          name = [name copyWithZone: NSDefaultMallocZone()];
          GSIMapAddPair(NAMED, (GSIMapKey)(id)name, (GSIMapVal)(void*)m);
          GS_CONSUMED(name)
        }
        else
        {
    
    
            //找到则直接获取相应的内部table
          	m = (GSIMapTable)n->value.ptr;
        }

        //内部table表中获取相应object对象作为key的节点
        n = GSIMapNodeForSimpleKey(m, (GSIMapKey)object);
        if (n == 0)
        {
    
    
          	//不存在此节点,则直接添加observer对象到table中
            o->next = ENDOBS;//单链表observer末尾指向ENDOBS
            GSIMapAddPair(m, (GSIMapKey)object, (GSIMapVal)o);
        }
        else
        {
    
    
          	//存在此节点,则获取并将obsever添加到单链表observer中
            list = (Observation*)n->value.ptr;
            o->next = list->next;
            list->next = o;
        }
    }
    //只有观察者对象情况
    else if (object)
    {
    
    
      	//获取对应object的table
        n = GSIMapNodeForSimpleKey(NAMELESS, (GSIMapKey)object);
        if (n == 0)
        {
    
    
          	//未找到对应object key的节点,则直接添加observergnustep-base-1.25.0
            o->next = ENDOBS;
            GSIMapAddPair(NAMELESS, (GSIMapKey)object, (GSIMapVal)o);
        }
        else
        {
    
    
          	//找到相应的节点则直接添加到链表中
            list = (Observation*)n->value.ptr;
            o->next = list->next;
            list->next = o;
        }
    }
    //处理即没有通知名称也没有观察者对象的情况
    else
    {
    
    
      	//添加到单链表中
        o->next = WILDCARD;
        WILDCARD = o;
    }
		//解锁
    unlockNCTable(TABLE);
}

ブロック形式のコードは次のとおりです。

- (id) addObserverForName: (NSString *)name 
                   object: (id)object 
                    queue: (NSOperationQueue *)queue 
               usingBlock: (GSNotificationBlock)block
{
    
    
    GSNotificationObserver *observer = 
        [[GSNotificationObserver alloc] initWithQueue: queue block: block];

    [self addObserver: observer 
             selector: @selector(didReceiveNotification:) 
                 name: name 
               object: object];

    return observer;
}

- (id) initWithQueue: (NSOperationQueue *)queue 
               block: (GSNotificationBlock)block
{
    
    
    self = [super init];
    if (self == nil)
        return nil;

    ASSIGN(_queue, queue); 
    _block = Block_copy(block);
    return self;
}

- (void) didReceiveNotification: (NSNotification *)notif
{
    
    
    if (_queue != nil)
    {
    
    
        GSNotificationBlockOperation *op = [[GSNotificationBlockOperation alloc] 
            initWithNotification: notif block: _block];

        [_queue addOperation: op];
    }
    else
    {
    
    
        CALL_BLOCK(_block, notif);
    }
}

blockオブジェクトの作成形式はGSNotificationObserverオブジェクトをBlock_copyコピーしてblock通知操作キューを決定し、通知受信処理関数didReceiveNotificationを渡してaddOperation指定された操作キューの処理を実装し、それ以外の場合は直接実行しますblock

通知を送信するためのコア関数の一般的なロジックは次のとおりです。

- (void) _postAndRelease: (NSNotification*)notification
{
    
    
    //入参检查校验
    //创建存储所有匹配通知的数组GSIArray
   	//加锁table避免数据一致性问题
    //获取所有WILDCARD中的通知并添加到数组中
    //查找NAMELESS表中指定对应消息发送者对象object的通知并添加到数组中
	//查找NAMED表中相应的通知并添加到数组中
    //解锁table
    //遍历整个数组并依次调用performSelector:withObject处理通知消息发送
    //解锁table并释放资源
}

上記の送信は、一致する通知をすべて取得して通知メッセージを送信することが中心なのでperformSelector:withObject、通知を送信するスレッドと通知を受信するスレッドは同じスレッドになります(キューを操作してblockキューの処理を指定する形式)。

通知のセクション

NS通知

NSNotificationnameメッセージ名、objectメッセージ送信者、userinfoメッセージ送信者によって伝送される追加情報など、メッセージ送信に関する情報が含まれます。そのクラス構造は次のとおりです。

@interface NSNotification : NSObject <NSCopying, NSCoding>

@property (readonly, copy) NSNotificationName name;
@property (nullable, readonly, retain) id object;
@property (nullable, readonly, copy) NSDictionary *userInfo;

- (instancetype)initWithName:(NSNotificationName)name object:(nullable id)object userInfo:(nullable NSDictionary *)userInfo API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

@end

@interface NSNotification (NSNotificationCreation)

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

- (instancetype)init /*API_UNAVAILABLE(macos, ios, watchos, tvos)*/;	/* do not invoke; not a valid initializer for this class */

@end

NSNotification オブジェクトは、インスタンスまたはクラスを通じて構築できます。(- (instancetype)initこのメソッドは初期化メソッドですが、NSNotificationクラス内では使用不可としてマークされています。したがって、このメソッドをオブジェクトのAPI_UNAVAILABLE初期化に直接使用することはできません。)NSNotification

NS通知センター

NSNotificationCenterメッセージ通知センター、グローバル シングルトン モード (各プロセスには、プロセス内通信用にデフォルトでデフォルト通知センターがあります)、次の方法で通知センターを取得します。

+ (NSNotificationCenter *)defaultCenter

macOS システムの場合、各プロセスにはデフォルトの分散通知センター NSDistributedNotificationCenter があります。詳細については、「NSDistributedNotificationCenter 」を参照してください。

通知メッセージの具体的な登録方法は以下のとおりです。

//注册观察者
- (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;
- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

登録されたオブザーバー メソッドには 2 つの形式があります:selectorblock、指定されたオブザーバー オブジェクトを追加するメソッドの場合はメソッドを実行しobserver使用される匿名オブザーバー オブジェクトを返し、メッセージを処理するオブザーバーの操作オブジェクトを指定します。nilblockcopyNSOperationQueue

指定されたメッセージ名nameと送信者オブジェクトはobject両方とも空にすることができます。これは、すべてのメッセージとすべての送信オブジェクトによって送信されたメッセージを受信することを意味します。一方または両方を指定した場合は、指定されたメッセージ名と送信者からメッセージを受信することを意味します。

blockメソッドで指定されたqueueキューの場合nil、デフォルトでメッセージを送信するスレッドで処理されます。メイン キューが指定されている場合は、UI 操作の実行によって発生する例外を回避するために、メイン スレッドによって処理されます。

注: オブザーバー通知メッセージを登録すると、登録が繰り返されることを避ける必要があります。登録が繰り返されると、通知メッセージの処理が繰り返され、block外部オブジェクトが保持されることになるため、循環参照の問題の発生を避ける必要があります。

メッセージの送信方法は以下のとおりです。

//发送消息
- (void)postNotification:(NSNotification *)notification;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject;
- (void)postNotificationName:(NSNotificationName)aName object:(nullable id)anObject userInfo:(nullable NSDictionary *)aUserInfo;

パッケージ化された通知メッセージ オブジェクトを介してメッセージを送信することNSNotificationも、メッセージ名、送信者、および送信する情報を指定することもできます。これは同期実行モードです。メソッドを実行する前に、登録されているすべてのオブザーバーが通知メッセージを処理するのを待つ必要があります。ブロックモードの場合は登録メッセージで指定されたキューで通知オブジェクトが実行され、block非メソッドの場合は通知オブジェクトは同一スレッドで処理されます。

注: メッセージ送信タイプは、登録時のタイプと一致している必要があります。つまり、登録されたオブザーバーがメッセージ名と送信者の両方を指定する場合、メッセージの送信時にメッセージ名と送信者も同時に指定する必要があります。そうしないとメッセージを受信できません。

オブザーバーを削除する方法は次のとおりです。

//移除观察者
- (void)removeObserver:(id)observer;
- (void)removeObserver:(id)observer name:(nullable NSNotificationName)aName object:(nullable id)anObject;

指定されたオブザーバーのすべての通知メッセージを削除できます。つまり、オブザーバーはメッセージを受信しなくなります。通常、deallocオブザーバー オブジェクトが解放された後に呼び出されますが、自動的に実行されるiOS9ためmacos10.11、その後手動で呼び出す必要はありません。dealloc加工された。

If your app targets iOS 9.0 and later or macOS 10.11 and later, you don't need to unregister an observer in its dealloc method. Otherwise, you should call this method or removeObserver:name:object: before observer or any object specified in addObserverForName:object:queue:usingBlock: or addObserver:selector:name:object:is deallocated.
翻译如下:
如果你的应用目标是iOS 9.0及更高版本或macOS 10.11及更高版本,你不需要在dealloc方法中注销观察者。否则,您应该调用此方法或在取消分配addObserverForName:object:queue:usingBlock:或addObserver:selector:name:object:中指定的任何对象之前删除观察者:name:object:方法。

NS通知キュー

NSNotificationQueue通知キューは、メッセージ送信のタイミング、メッセージのマージ戦略などの通知メッセージの管理を実装し、先入れ先出し方式でメッセージを管理しますが、実際のメッセージの送信は依然として通知センターを通じて完了しますNSNotificationCenter

@interface NSNotificationQueue : NSObject
@property (class, readonly, strong) NSNotificationQueue *defaultQueue;

- (instancetype)initWithNotificationCenter:(NSNotificationCenter *)notificationCenter NS_DESIGNATED_INITIALIZER;

- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle;
- (void)enqueueNotification:(NSNotification *)notification postingStyle:(NSPostingStyle)postingStyle coalesceMask:(NSNotificationCoalescing)coalesceMask forModes:(nullable NSArray<NSRunLoopMode> *)modes;

- (void)dequeueNotificationsMatching:(NSNotification *)notification coalesceMask:(NSUInteger)coalesceMask;

defaultQueue現在のスレッドにバインドされた通知メッセージ キューを取得することも、通知管理センターを指定することもできますinitWithNotificationCenter:具体的なメッセージ管理戦略は次のとおりです。

NSPostingStyle: 通知をいつ送信するかを設定するために使用されます

  • NSPostASAP: 現在の通知が呼び出されたとき、またはタイマーが終了したときに通知を送信します。
  • NSPostWhenIdle: ランループがアイドル状態になったときに通知します
  • NSPostNow: マージ通知が完了したらすぐに通知を送信します

NSNotificationCoalescing(これは であることに注意してくださいNS_OPTIONS): 通知をマージする方法を設定します。

  • NSNotificationNoCoalescing:合併不成立のお知らせ
  • NSNotificationCoalescingOnName: 通知名に従って通知を結合します
  • NSNotificationCoalescingOnSender: 受信オブジェクトに応じて通知をマージします

NSNotificationQueue通知キューで即時送信モードが指定されていない場合は、次の方法で非同期に送信NSPostNowできます。runloop

NSNotification とマルチスレッド

マルチスレッドに関するNSNotification公式ドキュメントは次のとおりです。

In a multithreaded application, notifications are always delivered in the thread in which the notification was posted, which may not be the same thread in which an observer registered itself.
翻译如下:
在多线程应用程序中,通知始终在发布通知的线程中传递,该线程可能与观察者注册自身的线程不同。

つまり、NSNotificationの送信処理と受信処理は同じスレッド内で行われ、ブロック形式の場合、受信処理は指定されたキューで処理されます。これについては上で説明しましたが、ここでは受信処理の処理方法に焦点を当てます。他のスレッドで。

For example, if an object running in a background thread is listening for notifications from the user interface, such as a window closing, you would like to receive the notifications in the background thread instead of the main thread. In these cases, you must capture the notifications as they are delivered on the default thread and redirect them to the appropriate thread.
翻译如下:
例如,如果在后台线程中运行的对象正在侦听来自用户界面的通知,例如窗口关闭,则您希望在后台线程而不是主线程中接收通知。在这些情况下,您必须在通知在默认线程上传递时捕获通知,并将其重定向到适当的线程。

公式が述べているように、バックグラウンド スレッドなど、メインスレッドではない通知スレッドの処理については、この処理シナリオが存在し、公式は具体的な実装計画も提供しています。

リダイレクトを実装する 1 つの方法は、通知キュー (NSNotificationQueue オブジェクトではなく配列であることに注意してください) をカスタマイズし、リダイレクトする必要がある通知をこのキューに保持させることです。いつものように通知オブザーバーを登録します。通知が届いたら、まず通知を投稿したスレッドが予期したスレッドかどうかを確認します。そうでない場合は、通知をキューに保存します。そして、目的のスレッドにマッハ シグナルを送信します。このスレッドに通知を処理する必要があることを伝えます。シグナルを受信した後、指定されたスレッドはキューから通知を削除して処理します。

公式デモは次のとおりです。

@interface MyThreadedClass: NSObject
/* Threaded notification support. */
@property NSMutableArray *notifications;
@property NSThread *notificationThread;
@property NSLock *notificationLock;
@property NSMachPort *notificationPort;
 
- (void) setUpThreadingSupport;
- (void) handleMachMessage:(void *)msg;
- (void) processNotification:(NSNotification *)notification;
@end

通知スレッド定義クラスにはMyThreadedClass、すべての通知メッセージを記録するための通知メッセージ キューnotifications、現在の通知受信スレッドを記録するnotificationThread、マルチスレッドの同時処理に必要なミューテックス ロックNSLock、および通知メッセージを処理するためのスレッド間通信のための通知処理スレッドが含まれますNSMachPort。設定スレッドのプロパティ、machメッセージを処理するためのインスタンス メソッド、および通知メッセージを処理するためのメソッドを提供します。

t メソッドは次のとおりですsetUpThreadSuppor

- (void) setUpThreadingSupport {
    
    
    if (self.notifications) {
    
    
        return;
    }
    self.notifications      = [[NSMutableArray alloc] init];
    self.notificationLock   = [[NSLock alloc] init];
    self.notificationThread = [NSThread currentThread];
 
    self.notificationPort = [[NSMachPort alloc] init];
    [self.notificationPort setDelegate:self];
    [[NSRunLoop currentRunLoop] addPort:self.notificationPort
            forMode:(NSString __bridge *)kCFRunLoopCommonModes];
}

これは主にクラス属性を初期化し、NSMachPortエージェントを指定して、それを処理スレッドに追加します。メッセージが到着し、受信スレッドが実行されていないrunloop場合、カーネルは次の実行までメッセージを保存します。実装することもできますが、必要があります子スレッドに対してオンにする必要があります。それ以外の場合、メソッドは無効になり、パラメータを非同期で呼び出すように指定する必要があります。machrunlooprunloopperformSelectro:inThread:withObject:waitUtilDone:modesrunloopwaitUtilDoneNO

NSMachPortDelegateプロトコル メソッドは次のように処理されます。

- (void) handleMachMessage:(void *)msg {
    
    
    [self.notificationLock lock];
 
    while ([self.notifications count]) {
    
    
        NSNotification *notification = [self.notifications objectAtIndex:0];
        [self.notifications removeObjectAtIndex:0];
        [self.notificationLock unlock];
        [self processNotification:notification];
        [self.notificationLock lock];
    };
 
    [self.notificationLock unlock];
}

NSMachPort プロトコル メソッドは主に、処理する必要がある通知メッセージをチェックし、それらを繰り返し処理します (多数のポート メッセージが同時に送信されてメッセージが失われることを防ぐため)。処理が完了すると、メッセージから削除されます。同期的にキューに入れます。

通知の処理方法は次のとおりです。

- (void)processNotification:(NSNotification *)notification {
    
    
    if ([NSThread currentThread] != notificationThread) {
    
    
        // 将通知转发到正确的线程。
        [self.notificationLock lock];
        [self.notifications addObject:notification];
        [self.notificationLock unlock];
        [self.notificationPort sendBeforeDate:[NSDate date]
                components:nil
                from:nil
                reserved:0];
    }
    else {
    
    
        // Process the notification here;
    }
}

プロトコル メソッドへの内部呼び出しと通知処理メッセージ コールバックを区別するにはNSMachPort、現在の処理スレッドを決定して、さまざまな通知メッセージ処理メソッドを処理する必要があります。通知監視コールバックの場合は、メッセージ キューにメッセージを追加し、スレッド間通信メッセージを送信します。 ; 実際、このmachソリューションの核心は、スレッド間の非同期通信を通じてNSMachPort、通知キュー内のメッセージを処理するように受信スレッドに通知すること。

受信スレッドでは、次のメソッドを呼び出して通知メッセージの処理を開始する必要があります。

[self setupThreadingSupport];
[[NSNotificationCenter defaultCenter]
        addObserver:self
        selector:@selector(processNotification:)
        name:@"NotificationName"//通知消息名称,可自定义
        object:nil];

同関係者は、この計画の問題点や考えについても次のように述べた。

まず、このオブジェクトによって処理されるすべてのスレッド通知は、同じメソッド (processNotification:) を通過する必要があります。第 2 に、各オブジェクトは独自の実装と通信ポートを提供する必要があります。より優れた、より複雑な実装では、動作が次のいずれかのサブクラスに一般化されます。 NSNotificationCenter またはスレッドごとに 1 つの通知キューを持ち、複数のオブザーバー オブジェクトとメソッドに通知を配信できる別のクラス まず、
この
オブジェクトによって処理されるすべてのスレッド通知は同じメソッド (processNotification:) を渡す必要があります。次に、各オブジェクトは独自の実装ポートと通信ポートを提供する必要があります。より良い、より複雑な実装では、動作を NSNotificationCenter のサブクラスまたは別のクラスに一般化し、スレッドごとに通知キューを備え、複数のオブザーバー オブジェクトとメソッドに通知を配信する機能を備えています。

NSNotficationCenterより良い方法は、自分でサブクラス化するか(github の一部の偉人がこのソリューションを実装しています。GYNotificationCenterを参照してください)、またはこの種の転送を処理する別のクラスを作成することであると指摘されています。

おすすめ

転載: blog.csdn.net/m0_63852285/article/details/132017911