Before iOS9.0, the notification center implemented unsafe_unretained reference for the observer object. When the referenced object is released, it will not be set to nil automatically. The pointer still points to the memory space, causing the wild pointer and EXC_BAD_ACCESS crash.

After iOS9, not removing an object does not cause a crash, because the notification center makes a weak reference to the observer and nullates the pointer to the object when it is destroyed. For the sake of rigor, it is best to register notifications as well as removeObserver during code writing.

So how does the notification center implement a reference to an observer? To understand this, let’s first look at the implementation mechanism for notifications. Since Apple does not open source the Foundation source code, we will refer to the GNUStep source code implementation. The source code for GNUStep is github.com/gnustep/lib… , the specific source can be viewed.

  • (void) addObserver: (id)observer selector: (SEL)selector name: (NSString*)name object: (id)object

Once you add an observer, you create an Observation object that stores the observer, SEL, etc. The structure of Observation is:

typedef	struct	Obs {
  id		observer;	/* Object to receive message.	*/
  SEL		selector;	/* Method selector.		*/
  struct Obs	*next;		/* Next item in linked list.	*/
  int		retained;	/* Retain count for structure.	*/
  struct NCTbl	*link;		/* Pointer back to chunk table	*/
} Observation;


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;

Copy the code

NSNOtifocation maintains GSIMapTable table structure, used to store the Observation, respectively is nameless, named, cache, nameless used to store did not name the incoming notification, named storage into the notice of the name, Cache Is used for fast caching. Nameless and named are both hash tables, but their structures are as follows:

In nameless table: GSIMapTable structure is as follows: Object: Observation object: Observation object: GSIMapTable: name: maptable name: maptable name: Maptable The structure of maptable is as follows: Object: Observation Object: Observation Object: ObservationCopy the code

Execute the following code:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification) name:@"Test" object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification) name:@"Test" object:@"12"];

[[NSNotificationCenter defaultCenter] postNotificationName:@"Test" object:@"123"userInfo:nil]; After the notification is sent, observers with the same name,object or object nil will receive the notification.Copy the code

Notifications are executed synchronously, in the same thread as the notification sending thread and notification receiving callback processing. The test code is shown below

 NSLog(@"Registration notification %@", [NSThread currentThread]);

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification) name:@"Test" object:@"123"];

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"Send notification %@", [NSThread currentThread]);
        [[NSNotificationCenter defaultCenter] postNotificationName:@"Test" object:@"123" userInfo:nil];
});
    
- (void)handleNotification {
    NSLog(@"Processing notification %@", [NSThread currentThread]);
}

Copy the code

Execution Result:

<NSThread: 0x2821F4a80 >{number = 1, name = main}

<NSThread: 0x2821F14C0 >{number = 5, name = (null)}

Handle notifications <NSThread: 0x2821F14C0 >{number = 5, name = (null)}**

By default, arrays hold all elements in an array. How can we implement a weak reference to the observer? Let’s start by looking at weak references to elements of collection type in Swift

Define a class:

class Pencil {
    var type: String
    var price: Double
    
    init(_ type: String, _ price: Double) {
        self.type = type
        self.price = price
    }
    
}
Copy the code
class CXDWeakArray: NSObject {

    func testRetainCount()  {
        
        print("Test begins \(CFGetRetainCount(Pencil("2B", 1.0) as CFTypeRef))") // The test beginslet pencil2B = Pencil("2B", 1.0)
        let pencilHB = Pencil("HB", 2.0)
                
        print("Object initialization \(CFGetRetainCount(pencil2B as CFTypeRef))")
        print("Object initialization \(CFGetRetainCount(pencilHB as CFTypeRef))") // Initialize the object 2 // Initialize the object 2let pencilBox = [pencil2B, pencilHB]
        
        print("Strong reference array \(CFGetRetainCount(pencil2B as CFTypeRef))")
        print("CFGetRetainCount(pencilHB as CFTypeRef)"// strong reference array 3}}Copy the code

WeakArray class:

WeakArray<Element: AnyObject> {
    private var items: [WeakBox<Element>] = []
    
    init(_ elements: [Element]) {
        items = elements.map{ WeakBox($0) }
    }
}

extension WeakArray: Collection {
    var startIndex: Int { return items.startIndex }
    var endIndex: Int { return items.endIndex }
    
    subscript(_ index: Int) -> Element? {
        return items[index].unbox
    }
    
    func index(after idx: Int) -> Int {
        return items.index(after: idx)
    }
    
}
Copy the code

The test code

        let weakPencilBox1 = WeakArray([pencil2B, pencilHB])
        
        print("Weak reference array \(CFGetRetainCount(pencil2B as CFTypeRef)))
        print("Weak reference array \(CFGetRetainCount(pencilHB as CFTypeRef))// Weak reference array 3 // weak reference array 3let firstElement = weakPencilBox1.filter {
            $0! = nil }.firstprint("Element type \(firstElement!! .type)"// Element type 2Bprint(\(CFGetRetainCount(pencil2B as CFTypeRef)))
        print(\(CFGetRetainCount(pencilHB as CFTypeRef))"Note: This is because firstElement holds (Retain) pencil2B, which increases its reference count by 1 */Copy the code

NSPointerArray

NSPointerArray is a replacement for Array, the main difference being that instead of storing objects, it stores Pointers to objects. Arrays of this type can manage weak references or strong references, depending on how they are initialized.

NSPointerArray.weakObjects()

NSPointerArray.strongObjects()
Copy the code
        let weakPencilBox2 = NSPointerArray.weakObjects()
        
        let pencil2BPoiter =  Unmanaged.passUnretained(pencil2B).toOpaque()
        
        let pencilHBPoiter = Unmanaged.passUnretained(pencilHB).toOpaque()
        print("PoiterArray initialization \(CFGetRetainCount(pencil2B as CFTypeRef))")
        print("PoiterArray initialization \(CFGetRetainCount(pencilHB as CFTypeRef))") 4 / / / / PoiterArray initialization PoiterArray initialization 3 weakPencilBox2. AddPointer (pencil2BPoiter) weakPencilBox2.addPointer(pencilHBPoiter)print(\(CFGetRetainCount(pencil2B as CFTypeRef))")
        print(\(CFGetRetainCount(pencilHB as CFTypeRef))") //PoiterArray End 4 //PoiterArray end 3Copy the code

Using NSPointerArray is useful for storing objects and keeping weak references, but it is not type-safe, and the compiler cannot infer the type of objects implicit within NSPointerArray because it uses Pointers to objects of type AnyObject.

Data structures of collection type, not just arrays, have strong references to their elements by default.

NSHashTable

        //NSHashTable -NSSet
        let weakPencilSet = NSHashTable<Pencil>(options: .weakMemory)
        weakPencilSet.add(pencil2B)
        weakPencilSet.add(pencilHB)
        
        
        print("NSHashTable Set \(CFGetRetainCount(pencil2B as CFTypeRef))")
        print("NSHashTable Set \(CFGetRetainCount(pencilHB as CFTypeRef))")
        //NSHashTable Set   4
        //NSHashTable Set   3
Copy the code

NSMapTable

class Eraser {
    var type: String
    
    init(_ type: String) {
        self.type = type
    }
}

     //NSMapTable -- NSDictionary
        let weakPencilDict = NSMapTable<Eraser, Pencil>(keyOptions: .strongMemory, valueOptions: .weakMemory)
        
        let paintingEraser = Eraser("Painting")
        
        weakPencilDict.setObject(pencil2B, forKey: paintingEraser)
        
        print("NSMapTable NSDictionary \(CFGetRetainCount(pencil2B as CFTypeRef))")
        print("NSMapTable NSDictionary \(CFGetRetainCount(pencilHB as CFTypeRef))")
        
        //NSMapTable NSDictionary   4
        //NSMapTable NSDictionary   3
Copy the code