1, the opening

This paper attempts to answer the following questions:

The dealloc object replaces the dealloc method with a block

Not class level control, object level control

2. Associated objects

3, the lock

This article is the interview according to the topic, the second

C… /CYLDeallocBlockExecutor

1, How to use block, dealloc goodbye

Because when an object is released, its associated objects are also released.

The block to be executed when an object is released is placed in the dealloc method of its associated object.

Additional:

typedef void (^VoidBlock)(void); @interface BlockExecutor : NSObject { VoidBlock block; } @property (nonatomic, readwrite, copy) VoidBlock block; - (id)initWithBlock:(VoidBlock)block; @end @implementation BlockExecutor @synthesize block; - (id)initWithBlock:(VoidBlock)aBlock { self = [super init]; if (self) { block = [aBlock copy]; } return self; } - (void)dealloc { if (block ! = nil) { block(); block = nil; } } @end const void *runAtDeallocBlockKey = &runAtDeallocBlockKey; @interface NSObject (RunAtDealloc) - (void)runAtDealloc:(VoidBlock)block; @end @implementation NSObject (RunAtDealloc) - (void)runAtDealloc:(VoidBlock)block { if (block) { BlockExecutor *executor = [[BlockExecutor alloc] initWithBlock:block]; objc_setAssociatedObject(self, runAtDeallocBlockKey, executor, OBJC_ASSOCIATION_RETAIN); } } @endCopy the code

call

- (void)viewDidLoad {
    [super viewDidLoad];
    NSObject * pikachu = [[NSObject alloc] init];
    [pikachu runAtDealloc:^{
        NSLog(@"Deallocating pikachu!");
    }];
}
Copy the code

Detailed three steps:

1, an associated class with a block, whose dealloc method executes the block

2. Add a class method to NSObject and pass a block to make it easy to call.

Associate class, observe class on association.

3. Observe the class call, passing in the block

Optimization:CYLDeallocBlockExecutorThe implementation of the

CYLDeallocBlockExecutor does an enhanced version.

Every NSObject is associated with a CYLDeallocCallbackModel,

Inside the CYLDeallocCallbackModel is a mutex, an event dictionary, and the caller


@interface CYLDeallocCallbackModel : NSObject

@property (nonatomic, assign) pthread_mutex_t lock;
@property (nonatomic, strong) NSMutableDictionary *callbacksDictionary;
@property (nonatomic, unsafe_unretained) id owner;

@end
Copy the code

When NSObject is destroyed, its associated CYLDeallocCallbackModel is also destroyed,

The added events are executed in sequence


@implementation CYLDeallocCallbackModel

- (void)dealloc {
    
    NSArray *sortedKeys = [[_callbacksDictionary allKeys] sortedArrayUsingSelector: @selector(compare:)];
    for (NSNumber *identifier in sortedKeys) {
        
        CYLDeallocSelfCallback block =  _callbacksDictionary[identifier];
        block(_owner, identifier.unsignedIntegerValue);
    }
    pthread_mutex_destroy(&_lock);
}

@end

Copy the code

The structure of the event dictionary is [Int: Block], locked, and the event dictionary is thread-safe. Both modification and deletion are protected by mutex

Add event dictionaries, serial thread queues to the class of NSObject to keep it safe

Instead of the previous one, the special effects are,

You can add destruction events to the same object more than once

/ / add a [self cyl_willDeallocWithSelfCallback: ^ (__unsafe_unretained typeof unsafeSelf (self), NSUInteger identifier) { // ... }]; / / add a second [self cyl_willDeallocWithSelfCallback: ^ (__unsafe_unretained typeof unsafeSelf (self), NSUInteger identifier) { // ... }];Copy the code

SuiYu:

When you want an object A to be released, release B. You can relate B to A

Fun With the Objective-C Runtime: Run Code at Deallocation of Any Object

This library, associated with references to Runtime, has a low performance cost, its authors say.

There are locks, singleton queues, and event dictionaries, which reflect the elegance and efficiency of the Apple Weak

There’s a paragraph in CYLDeallocBlockExecutor,

- (NSHashTable *)cyl_deallocExecutors { NSHashTable *table = objc_getAssociatedObject(self, @selector(cyl_deallocExecutors)); if (! table) { table = [NSHashTable hashTableWithOptions:NSPointerFunctionsStrongMemory]; objc_setAssociatedObject(self, @selector(cyl_deallocExecutors), table, OBJC_ASSOCIATION_RETAIN); } return table; }Copy the code

Is equivalent to

- (NSString *)cyl_deallocExecutors { NSString *table = objc_getAssociatedObject(self, @selector(cyl_deallocExecutors)); if (! Table) {table = @" ha ha "; objc_setAssociatedObject(self, @selector(cyl_deallocExecutors), table, OBJC_ASSOCIATION_RETAIN); } return table; }Copy the code

You can have all kinds of equivalents

The key of the associated object is essentially unique

mystifying

CYLDeallocBlockExecutor author says its performance is good.

Use locks and serial queues to increase wait and overhead

The big guy’s brain circuits…

Storytelling: Why is this library locked?

B 1, how to show that someone has read the Runtime source code without locking

B, 2. Lock it. It’ll dissuade the little white. Small white see easy sleepy, healthy…

B, 3, lock. No, no, no. Use the function, the last lock protection is no problem.

Deadlock correlation can be further grasped

A, 1, lock dictionary changes, thread-safe.

A 2, use serial queue to ensure the write security of self-increasing key.

This serial queue, one for each object.

A global serial queue is used to ensure that only one associated serial queue is instantiated per object

Static dispatch_queue_t _deallocCallbackQueueLock = NULL; @implementation NSObject (CYLDeallocBlockExecutor) - (dispatch_queue_t)cyl_deallocCallbackQueue { Is a singleton static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSString *queueBaseLabel = [NSString stringWithFormat:@"com.chengyilong.%@", NSStringFromClass([self class])]; const char *queueName = [[NSString stringWithFormat:@"%@.deallocCallbackQueueLock", queueBaseLabel] UTF8String]; _deallocCallbackQueueLock = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL); }); In the global serial queue, // ensure that each NSObject is associated with a serial queue with only one __block dispatch_queue_t queue = nil; dispatch_sync(_deallocCallbackQueueLock, ^{ dispatch_queue_t deallocCallbackQueue = objc_getAssociatedObject(self, @selector(cyl_deallocCallbackQueue)); if (deallocCallbackQueue) { queue = deallocCallbackQueue; } else { NSString *queueBaseLabel = [NSString stringWithFormat:@"com.chenyilong.%@", NSStringFromClass([self class])]; NSString *queueNameString = [NSString stringWithFormat:@"%@.forDeallocBlock.%@",queueBaseLabel, @(arc4random_uniform(MAXFLOAT))]; const char *queueName = [queueNameString UTF8String]; queue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL); self.cyl_deallocCallbackQueue = queue; }}); return queue; } @endCopy the code

2. Introduce the principle of associated objects

This is also a hash table, which is similar to weak

Each time an association is set up, it is managed with the associated object hash table that comes with an object

  • The association manager, AssociationsManager, has spin locks and hash tables for associated objects

Through the spin lock, the operation of the hash table of the associated object is guaranteed to be thread safe

  • Hash table AssociationsHashMap (object: object association table)

[key: object association] [key: object association]

Object is associated with objcasSociety and contains the associated values and memory management policy

When you have structure, you can make it up

Three operations,

1. Add an associated object as a key-value pair

You take the object, you look for the ObjectAssociationMap, you don’t find it, you initialize it, you fill in the data

Find the ObjectAssociationMap, hold the key, find the object associated with ObjcAssociation, if not found, add an ObjcAssociation

The object association objcasSociety is found and the information is refreshed

The data structure is fixed, the operation is simple, the following two are similar

2. Obtain the associated object based on key

3. Remove all associated objects

Additional:

In the examples above, the parameters are

object, key , value , policy

objc_setAssociatedObject(self,
                                 runAtDeallocBlockKey,
                                 executor,
                                 OBJC_ASSOCIATION_RETAIN);

Copy the code

3. Lead out the lock

Common locks, there are many. Spinlocks, mutexes and semaphores are described below

Locks are used to solve concurrency problems.

Spin-locks and mutexes can both be mutually exclusive and mutually exclusive

Semaphores, you can do more.

  • With a spinlock, the thread that did not acquire the lock keeps trying to acquire it, doing nothing useful.

The benefit is that the overhead of thread context switching is reduced.

If the lock operation is not time consuming, use spin lock

If the lock operation is time consuming, it is a waste of CPU

  • Mutex,

The thread has an ownership for the lock

If thread A acquires the lock, other threads cannot access the resource corresponding to the lock.

(Other threads do other things, there is a thread context-switching which moves the top pointer of the stack and copies related parameters onto the function stack)

Until thread A releases the lock

(Other threads are aroused to acquire the lock.)

The actions are lock and unlock

  • Semaphore,

The number of threads allowed to operate on the resource corresponding to the lock at the same time

Semaphore uses a signaling mechanism that, unlike Mutex, does not hold a thread to a lock

Wait (signal value -1) and signal (signal value + 1),

The signal value is 0, the next thread that accesses, suspends hanging

They ask you, colloquial

The thread to get the lock, compared to several people to go to the bathroom.

  • Spin locks and mutex, both are toilets only one

Threads use spin locks, toilets are occupied, they’re always waiting.

The thread uses the mutex, the toilet is occupied, and it goes back to work. Thread sleep, return to thread queue. The CPU corresponding to this thread has no empty consumption.

Threads acquire and hold locks,

When done, the thread hands over the lock to another thread

  • Semaphores are toilets with multiple

The semaphore is set to how many toilets there are

additional

Spin locks are an old solution,

Spin, looping, keep getting locks

Spin lock is a busy waiting lock

Mutex is sleeping lock

spinlocks

The mutex

Differences between spin locks and other locks

The difference between mutex and semaphore

Finally, this article does not…

This article is also called “Sage Time: A Brief Reading of the XXX Open Source Library”.

This article is for your reference only

About… DeallocBlockExecutor, maybe some skill is not…

In case you don’t find any…