Analysis of hash table structure

We saw earlier in @Synchronization that you can have this many tables

class StripedMap {
#ifTARGET_OS_IPHONE && ! TARGET_OS_SIMULATOR
    enum { StripeCount = 8 };
#else
    enum { StripeCount = 64 };
#endif
    // ...
}
Copy the code

So what is a hash table? We find the structure of the SideTable, and we find a weak reference table and a reference counting table

struct SideTable {
    spinlock_t slock;
    RefcountMap refcnts;
    weak_table_t weak_table;
	/ /...
};
Copy the code

A weak reference table

So before we do that, let’s take a quiz. What’s the print out below

NSObject *objc = [[NSObject alloc]init];
NSLog(@"%ld -%@", (long)CFGetRetainCount(objc), objc);
__weak typeof(id) weakObjc = objc;
NSLog(@"%ld -%@", (long)CFGetRetainCount(objc), objc);
NSLog(@"%ld -%@", (long)CFGetRetainCount(weakObjc), weakObjc);
Copy the code

Why is the third one two? Just follow the conventional thinking and break points, anddebugOpen the assembly

LibObjc: objc_initWeak -> storeWeak…. This structure is similar to the associative object

  1. Find the hash table first
  2. Find the weak reference table of weakTable in the hash table
  3. To create aweak_entry_t
  4. thereferentTo join theweak_tableAn array ofinline_referrers
  5. >= TABLE_SIZE(entry) * 3/4Weak_table capacity
  6. thenew_entryTo join theweak_tableIn the

Then continue tostep overI’m going to go to this methodFind the libobJC source codeObjc_loadWeakRetained this methodStep by step debuggingNotice that at line 563 the reference count is still 1, but at line 566 the reference count is 2. We can guess from this that weak references are inrootTryRetainWeak references can be +1, so I’ll print it again. Is count 3? So let’s verify thatWeak reference when the current object is copied to a reference count table, weakObjc operates reference count from its own weak reference table. Weak reference tables and ARC strong references are independent of each other,

TimerThe strong holds

What’s wrong with the following operation?

self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
Copy the code

We destroy the timer in the dealloc method, but when the page pop returns the page is not released, so the timer keeps going. This creates a circular reference. Let’s try the weak modifier self

popIt still wasn’t destroyed when it came back, so why?command + shift + OFind the official documentation

What is stated here is that it is strongly held until the Timer is destroyed, so even if we use weakSelf modification, circular reference is still created here.

Solution 1: Use the following method, which does not hold self strongly

scheduledTimerWithTimeInterval:repeats:block:
Copy the code

Solution 2: The mediator mode Target does not use self FBKVO thinking

RunLoop -> Timer -> self -> Timer RunLoop -> Timer -> TimerProxy has no operation on self, so self can destroy the Timer on dealloc, and the Timer automatically disconnects from TimerProxy.

public class WeakTimerProxy: NSObject {
    weak var target: NSObjectProtocol?
    var sel: Selector?
    
    /// required, the timer must be assigned to the proxy after the timer is instantiated, otherwise the timer itself will continue to run even if target is freed
    public weak var timer: Timer?
    
    public required init(target: NSObjectProtocol? .sel: Selector?). {
        self.target = target
        self.sel = sel
        super.init(a)// Strengthen security protection
        guard target?.responds(to: sel) = = true else {
            return
        }
        
        // Replace target's selector with redirectionMethod, which reprocesses the event
        let method = class_getInstanceMethod(self.classForCoder, #selector(WeakTimerProxy.redirectionMethod))!
        class_replaceMethod(self.classForCoder, sel!, method_getImplementation(method), method_getTypeEncoding(method))
    }
    
    @objc func redirectionMethod(a) {
        // If target is not released, the target method is called, otherwise the timer is released
        if self.target ! = nil {
            self.target!.perform(self.sel)
        } else {
            self.timer?.invalidate()
            FCLog("Timer destroyed.")}}}Copy the code