0.0.1, dealloc block, tell a story, IOS interview questions

Time:2022-5-13

1. Opening

This paper attempts to answer the following questions:

When an object is used instead of an alldeoc method

Not class level control, but object level control

2. Associated object

3. Lock

This article is the second part of the interview

This article has reference c/ CYLDeallocBlockExecutor

1. How to make good use of block and let dealloc see you again

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

When releasing an object, the block to be executed 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);
    }
}

@end

call

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

Detailed three steps:

1. An association class with block executes block in its dealloc method

2. Add a classification method to nsobject and pass block for easy calling.

Associate a class with an observation class.

3. Observe the class call and pass in block

Optimization:CYLDeallocBlockExecutorImplementation of

CYLDeallocBlockExecutorIt’s an enhanced version.

Each nsobject is associated with aCYLDeallocCallbackModel,

CYLDeallocCallbackModelThere is a mutex, an event dictionary, and a caller


@interface CYLDeallocCallbackModel : NSObject

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

@end

When nsobject is destroyed, it is associatedCYLDeallocCallbackModelWill also be 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

The structure of the event dictionary is [int: block], locked, and the event dictionary is thread safe. Modification and deletion are protected by mutex

Add event dictionary and serial thread queue to the classification of nsobject to ensure safety

Compared with the previous one, the special effects added are,

Destruction events can be added multiple times for the same object

//Add once
[self cyl_willDeallocWithSelfCallback:^(__unsafe_unretained typeof(self) unsafeSelf, NSUInteger identifier) {
        // ...
        
    }];
    
//Add second
    [self cyl_willDeallocWithSelfCallback:^(__unsafe_unretained typeof(self) unsafeSelf, NSUInteger identifier) {
        // ...
    }];

Broken words:

When you want an object a to be released, release B. Just connect B to a

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

The author of this library, which refers to runtime, said that the performance consumption is low.

There are locks, a single case queue and an event dictionary, which reflect the elegance and efficiency of Apple weak

CYLDeallocBlockExecutorThere’s a paragraph in it,


- (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;
}

Equivalent to

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

Various equivalents can also be used

The key of the associated object is essentially unique

kick up a cloud of dust

CYLDeallocBlockExecutorThe author said that its performance is good.

Lock and serial queue are used to increase waiting and overhead

Big guy’s brain circuit

Story telling: why does this library use locks?

B 1. No lock. How does it reflect that someone has read the runtime source code

B 2. Use the lock to persuade Xiaobai to retreat. Xiaobai is easy to feel sleepy and healthy

B 3, lock, no white, No. For functions that cannot be used, the last lock protection is no problem.

It can also further grasp the deadlock correlation

A 1. Use lock to ensure dictionary modification and thread safety.

A2. Serial queue is used to ensure the write security of key self increment.

In this serial queue, each object is associated with one.

Then use a global serial queue to ensure that each object will instantiate only one associated serial queue

//Global serial queue
static dispatch_queue_t _deallocCallbackQueueLock = NULL;

@implementation NSObject (CYLDeallocBlockExecutor)
- (dispatch_queue_t)cyl_deallocCallbackQueue {

    //Global serial queue, 是一个单例
    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);
    });
    
    // _ Dealloccallbackqueuelock is in the global serial queue,
    //Ensure that there is only one serial queue associated with each nsobject
    __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;
}
@end

2. Principle of exporting associated objects

It is also a hash table, similar to the principle of weak

Each time the association is set, it is managed by the associated object hash table attached to an object

  • The association manager, associationsmanager, has spin locks and a hash table of associated objects

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

  • The structure of association object hash table associationshashmap can be understood as [object: object association table]

The structure of the object association table objectassociationmap can be understood as [key: object association]

Object is associated with objcassociation, which contains the associated value and memory management policy

With structure, it’s easy to make up

Three operations,

1. Add associated objects in the form of key value pairs

Take the object and find the object association table objectassociationmap. If it is not found, initialize and fill in the data

Find the objectassociationmap, take the key and find the object association objcassociation. If not, add an objcassociation

When the object association objcassociation is found, the information is refreshed

The data structure is determined and the operation is simple. The following two are similar

2. Get the associated object according to the key

3. Remove all associated objects

Additional:

In the example above, the parameters are

object, key , value , policy

objc_setAssociatedObject(self,
                                 runAtDeallocBlockKey,
                                 executor,
                                 OBJC_ASSOCIATION_RETAIN);

3. Lead out lock

There are many common locks. Spinlocks, mutexes, and semaphores are described below

Locks are used to solve concurrency problems.

Spin lock and mutex lock can be mutually exclusive

Semaphore, more can be done.

  • Using spin lock spinlock, threads that do not acquire locks will always try to acquire locks without doing anything useful.

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

If the operation corresponding to the lock is not time-consuming, it is suitable to use spin lock

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

  • Mutex,

Thread to lock, there is a holding relationship, ownership

When thread a obtains the lock, other threads cannot access the resources corresponding to the lock,

(other threads do other things. One thread’s context switching is to move the pointer at the top of the stack and copy relevant parameters to the function stack)

Until thread a releases the lock

(other threads are aroused and can acquire locks)

The operations are lock and unlock

  • Semaphore,

The number of threads allowed to operate the resources corresponding to the lock at the same time depends on the signal value

Semaphore uses a signaling mechanism,Compared with mutex, threads have no relationship with locks

The operations are wait (signal value – 1) and signal (signal value + 1),

When the signal value is 0, the next accessed thread will suspend hanging

The other party asks you, popular

Compare the thread obtaining lock to several people going to the bathroom.

  • Spin lock and mutex lock are both toilets, and there is only one

The thread uses a spin lock. Someone in the toilet has been waiting there.

Threads use mutex locks. When there is someone in the toilet, they go back to work. Thread sleep, return to the thread queue. The CPU corresponding to this thread has no empty consumption.

Threads acquire and hold locks,

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

  • Semaphore, that is, there are multiple toilets

The semaphore setting is the number of toilets

additional

Spin lock is a very old solution,

spin, yesloopingAlways cycle to obtain the lock

Spin lock, yesbusy waiting lock

Mutex, yessleeping lock

Spin lock
0.0.1, dealloc block, tell a story, IOS interview questions

mutex

0.0.1, dealloc block, tell a story, IOS interview questions

The difference between spin lock and other locks

0.0.1, dealloc block, tell a story, IOS interview questions

The difference between mutex and semaphore

0.0.1, dealloc block, tell a story, IOS interview questions

End, this article does not

This article is also known as “sage time: shallow reading of XXX big man’s open source library”

This article is for reference only, suitable for temporary cramming

about...DeallocBlockExecutorMy evaluation, maybe some skill is not

In case you don’t find one of them