“This is the 18th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

NSRecursiveLock

NSRecursiveLock concept

In real development, NSLock must call UNLOCK before calling lock. NSRecursiveLock is a recursive lock that is used when performing recursive operations. A recursive lock in a mutex that can be acquired more than once by the same thread without causing a deadlock. In layman’s terms, a thread has acquired a lock and starts executing code protected by the lock (which has not yet been unlocked). If this code calls another function and the function that is called acquires the lock, the lock can already be acquired and executed without deadlock, provided that the lock is on the same thread

NSRecursiveLock common < complyNSLockingAgreement >

@protocol NSLocking

- (void)lock;
- (void)unlock;

@end

@interface NSLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

@end
Copy the code

NSRecursiveLock Common scenario

Let’s start with a typical deadlock situation

Example:

NSLock *lock = [[NSLock alloc] init];

dispatch_async(dispatch_get_global_queue(0.0), ^ {static void (^recursiveMethod)(int);
   recursiveMethod = ^(int value){
        
       [lock lock];
       if (value > 0) {
           NSLog(@"value==%d",value);
           sleep(2);
           recursiveMethod(value - 1);
       }
       [lock unlock];
   };
   recursiveMethod(5);
});

log:
value==5
Copy the code

Simple analysis:

RecursiveMethod is called recursively. So every time it enters this block, it adds a lock, and from the second time, since the lock is already in use and not unlocked, it has to wait for the lock to be unlocked, which causes a deadlock, and the thread is blocked, so the console just prints out one line

In this case, NSRecursiveLock can be used. A recursive lock keeps track of how many times it is locked. Each successful lock must balance calls to unlock. Only when this balance is reached can the lock finally be released for use by other threads

Example:

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];

dispatch_async(dispatch_get_global_queue(0.0), ^ {static void (^recursiveMethod)(int);
    recursiveMethod = ^(int value){
        [lock lock];
        if (value > 0) {
            NSLog(@"value==%d",value);
            recursiveMethod(value - 1);
        }
        [lock unlock];
    };
    recursiveMethod(5);
});
Copy the code

The log: