Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities

selfsuperWhat do they mean respectively

// LGTeacher inherits from LGPerson
- (instancetype)init{
    self = [super init];
    if (self) {
        NSLog(@"% @ - % @",[self class],[super class]);/ / print LGTeacher LGTeacher
    }
    return self;
}
Copy the code

Self is the parameter name, and super is the keyword, where [self class] actually calls NSObject’s class method. Objc_msgSend (void/* id self, SEL op… */), the recipient is self (LGTeacher); Objc_msgSendSuper (void/* struct objc_super *super, SEL op… */) is a struct objc_super.[super class] objc_msgSendSuper note: there is a line comment here that gets the current class and not its parent, LGTeacher

Block inspection

What does the following console output

NSObject *objc = [NSObject new];
NSLog(@"%ld".CFGetRetainCount(__bridge CFTypeRef)(objc))); / / 1

void(block1)(void) = ^ {NSLog(@"---%ld".CFGetRetainCount((__bridge CFTypeRef(objc)));/ / 3
}
block1();

void(^ __weak block2)(void) = ^ {NSLog(@"---%ld".CFGetRetainCount((__bridge CFTypeRef)(objc)));/ / 4
}
block2();

void(block3)(void) = [block2 copy]; / / 5
block3();

__block NSObject *obj= [NSObject new]; 
void(^block4)(void) = ^ {NSLog(@"---%ld".CFGetRetainCount((__bridge CFTypeRef)(obj))); / / 1
}
block4();
Copy the code
  • The first one is printed as 1 without a doubt
  • The second normal block captures external variables, makes a copy of the object’s attributes as its own member variables, and then copies a copy. assembly__block_copyTime is 2, go toNSMallocBlockIt becomes 3
  • The third __weak modifier is a stack block that only does +1
  • The fourth copy + 1,234 is verifiedstrongBlock = __weak weakBlock + [weakBlock copy]

GCD implements read and write locks

Read/write locks have the following features:

  • Only one thread can write at a time
  • Multiple threads are allowed to read at the same time
  • Both write and read operations are not allowed at the same time
/ / write
- (void)safeSetter: (NSString *)name time:(int)time {
    dispatch_barrier_async(self.safeQu, ^{
        sleep(time);
        [self.dict setValue:name forKey:@"test"];
        NSLog(@" say %@ - %@".self.dict[@"test"], [NSThread currentThread]);
    });
}
Copy the code
/ / read
- (NSString *)safeGetter {
    __block NSString * result;
    // add a synchronization to ensure that the current thread reads correctly or result returns null
    dispatch_sync(self.safeQu, ^{
        result = self.dict[@"test"];
    });
    return result;
}
Copy the code

Dispatch_barrier_async: dispatch_barrier_async: dispatch_barrier_async: dispatch_barrier_async: dispatch_barrier_async: dispatch_barrier_async: dispatch_barrier_async: dispatch_barrier_async

Why is @synchronized the most frequently used

  • Synchronized encapsulates a recursive lock that can be automatically unlocked
  • LockCount in @synchronized controls recursion, while threadCount controls multithreading, addressing the reentrant recursion of locks
  • @synchronized is also the easiest way to write a lock

Block type, how to distinguish?

MallocBlock: in the heap, with local variables or OC attributes inside the block and assigned to strongly referenced or copy-modified variables StackBlock: Blocks on the stack, like blocks on the heap, can use local variables or OC attributes internally, but cannot be assigned to strongly referenced or copy-modified variables

Setter procedure for KVC ordinary objects

  1. For example, in this article, the order of names issetName->_setName->setIsName. When any of these three setters are found, assignment is performed
  2. judge+ (BOOL)accessInstanceVariablesDirectlyWhether the function returns YES. If YES is returned, the_key->_iskey->key->iskeySearch for members in the order of, and assign to any one found. If NO is returned, the object is executedSetValue: forUndefinedKey:Function that throws an exception by default

KVO underlying principle mechanism analysis

KVO is implemented based on the Runtime mechanism. KVO uses isa-Swizzling technology, which is the type mixed pointer mechanism. The ISA Pointers of two objects are swapped with each other, which is commonly known as black magic.

When a property object is observed for the first time, the system dynamically creates a derived class NSKVONotifying_xxx of that class at run time. This derived class overrides any setter methods of the observed property in the base class. The derived class implements the real notification mechanism in the overridden setter.

Each class object has an ISA pointer to the current class. When a class object is observed, the system secretly points the ISA pointer to a dynamically generated derived class, thus performing setter methods of the derived class when assigning values to monitored properties

The key-value observation notification relies on NSObject’s two methods, willChangeValueForKey: and didChangeValueForKey. Before an observed property is changed, willChangeValueForKey: must be called, which records the old value. When change after didChangeValueForKey is called, and then through the message or response mechanism to invoke observerValueForKey: ofObject: change: context: when removeObserver isa refers to come back again.

Parameter pushing and structure pushing order

What happens when a stack frame is pushed? What you’re really looking at is the order in which parameters and structures are pushed

- (void)viewDidLoad {// parameter push (id self, sel _cmd)
    [super viewDidLoad];// Structure stack (objc, class)
    Class cls = [Person class];
    void  *kc = &cls; // kc does not push the pointer address of the class
    [(__bridge id)kc saySomething];
    Person *person = [Person alloc];
}
Copy the code

Self -> CMD (viewDidLoad) -> Self -> Self -> Person -> Person

How and why do iOS threads survive

The thread will be destroyed when the task is completed. If you want to keep the thread immortal, you need to add a runloop to the thread. In practical development, you will often encounter some time-consuming and frequent work, which is not related to the UI, such as downloading large files. The background reports data at intervals, and APM starts a Watch Dog thread.

NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
[runLoop run];
Copy the code

How can a thread that is alive be reclaimed?

Self refers to thread,thread refers to self, and thread refers to self in a loop
KCThread *thread = [[KCThreadalloc] initWithTarget: self selector: @selector(sayNB) object:nil];
- (void)exitThread {
	// The following Settings are required for recycling
	self.stopped=YES;
	/ / stop runloop
	CFRunLoopStop(CFRunLoopGetCurrent());
	[self.thread cancel];
	// Solve the circular reference problem
	self.thread=nil;
	NSLog(@"%s%@",__func__,[NSThreadcurrentThread]);
}
Copy the code

Circular reference, why add strong to block

Weak Typeof (self) Weaksel self although weak reference weakSelf solves the problem of circular reference without method release but there will be a premature release problem!

For example, add a delay inside the block, but the user moves too fast, causing self to release prematurely! We use __strong __typeof(weakSelf) strongSelf = weakSelf; Extended the life cycle so that strongSelf is valid in scope and can be recycled in time out of scope, since strongSelf is a temporary variable

dispatch_onceThe underlying

void
dispatch_once_f(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
	dispatch_once_gate_t l = (dispatch_once_gate_t)val;

#if! DISPATCH_ONCE_INLINE_FASTPATH || DISPATCH_ONCE_USE_QUIESCENT_COUNTER
	uintptr_t v = os_atomic_load(&l->dgo_once, acquire);
	if (likely(v == DLOCK_ONCE_DONE)) {
		return;
	}
#if DISPATCH_ONCE_USE_QUIESCENT_COUNTER
	if (likely(DISPATCH_ONCE_IS_GEN(v))) {
		return _dispatch_once_mark_done_if_quiesced(l, v);
	}
#endif
#endif
	if (_dispatch_once_gate_tryenter(l)) {
		return _dispatch_once_callout(l, ctxt, func);
	}
	return _dispatch_once_wait(l);
}
Copy the code

Enter the source code of dispatch_once_F, where val is the onceToken static variable passed in from the outside world, and func is dispatch_Block_invoke(block), where the underlying singleton is mainly divided into the following steps

  • Convert val, a static variable, todispatch_once_gate_tType variable
  • throughos_atomic_loadGets the identifier v for the task at this time
  • If v is equal toDLOCK_ONCE_DONEReturn indicates that the task has been executed
  • If locking fails after the task is executed, go todispatch_once_mark_done_if_quiescedFunction to store again, setting the identifier toDLOCK_ONCE_DONE
  • Otherwise, it passes_dispatch_once_gate_tryenterTry to enter the task to unlock, then executedispatch_once_callout Perform the block callback
  • If there is a task in progress at this time, the next task 2 passesdispatch_once_waitThe function puts task 2 into an infinite wait

What is iOS multithreading and thread lifecycle

For a single-core CPU, the CPU can only handle one thread at a time, that is, only one thread is working. The essence of simultaneous execution of multiple threads in iOS is that the CPU directly switches between multiple tasks quickly. Due to the fast enough time of CPU scheduling threads, the effect of simultaneous execution of multiple threads is caused. The time interval for switching is the time slice. Multicore cpus are truly concurrent!

  1. New state: YesnewKeyword after a thread is created, the thread object is in the new state. The thread in the nascent state has its own memory space, through callsstart()Method enters the ready state.
  2. Ready: A thread in the ready state is ready to run, but has not been allocated CPU. It is in the thread ready queue, waiting for the system to allocate CPU for it. When the system selects a thread to be executed, it moves from the ready state to the running state. This action is called “CPU scheduling.”
  3. Running state: The thread in the running state executes its ownrunMethod until the code blocks waiting for a resource or dies after completing a task. If the execution is not completed within a given time slice, it is replaced by the system and returned to the ready state.
  4. Blocked: a thread in a running state under certain conditions, such as executionSleep (sleep)Method, or waiting for a resource such as an I/O device, will free up the CPU and temporarily stop running itself, entering a blocked state. A thread in the blocked state cannot enter the ready queue. Only when the cause of the blocking is eliminated, such as when it is time to sleep, or when the waiting I/O device is idle, will the thread go to the ready state and return to the ready queue, waiting to be selected by the system and resume execution from where it left off.
  5. Dead state: The dead state is the last phase in the life cycle of a thread. There are three reasons why a thread dies. One is that a thread that is running properly has done all its work. Another is mandatory termination of a thread, such as the exit method to terminate a thread [not recommended]. Third, the thread throws an uncaught exception.

Briefly describe the principles of semaphores and scheduling groups

Semaphore function is usually used to synchronize the execution of tasks, similar to mutex, the user can control the maximum number of concurrent GCD! Dispatch_semaphore_create Indicates the maximum number of concurrent dispatch_semaphore_WAIT dispatch_SEMapHOre_wait dispatch_SEMaphore_signal dispatch_semaphore_signal release +1

  1. Create a semaphore and control the amount of trafficdispatch_semaphore_t sem = dispatch_semaphore_create(1)
  2. dispatch_semaphore_waitThe underlying source code is an operation of the original semaphore -1, when our signal value is less than 0 will enter the judgment waiting time. If set toDISPATCH_TIME_FOREVERTo get intodo-whileAn infinite loop waits for a signal to initiate
  3. dispatch_semaphore_signalThe principle of
  • insideos_atomic_inc2oThe atomic operation increments itself by 1, and then it decides ifvalue>0, returns 0.
  • For example,value+1And then it’s still less than 0, which means it’s a negative number, which is the calldisatch_semaphore_waitThat’s too many times. It’s still less than 0 and==LONG_MINSo unusualUnbalanced call to dispatch_semaphore_signal()
  • And then it calls_dispatch_semaphore_signal_slowMethod to do fault tolerant processing,_dispatch_semaphore_signaIs ado-whileCycle callsemaphore_siqnalSo you can responddispatch_semaphore_waitStuck in an endless loop of waiting

The most direct function of a scheduling group is to control the execution sequence of tasks

  • dispatch_group_create: create a group
  • dispatch_group_async: Group tasks
  • dispatch_group_notify: Notifies the group of the task completion
  • dispatch_group_wait: Indicates the waiting time of the group task execution
  • dispatch_group_enter: into the group
  • dispatch_group_leave: a group of
  1. dispatch_group_createCreate groups to control the status of all scheduling groups
  2. Group in and group out
  • Dispatch_group_enter: dispatcher group value-1. Waiting for the signal

  • Old_value == DISPATCH_GROUP_VALUE_MAX indicates Too many calls to dispatch_group_enter()

  • “Dispatch_group_leave” : dispatch_group_leave: “value+1”, “dispatch_group_wake” : “group_enter” : “group_leave” : “group_leave” : “group_leave” : “group_leave” : “group_leave” : “group_leave” : “group_leave” : “group_leave” : “group_leave” : Then dispatch_continuation_async is executed

  • Dispatch_group_leave +1 or value=0 indicates that the incoming and outgoing groups do not match

  1. dispatch_group_async The group task is encapsulateddispatch_group_enterInto the group of +dispatch_group_leaveA set of
  2. dispatch_group_notify: whenold_state=0Is called when_dispatch_group_wakeThat’s what calls the blockcallout, andleaveCalling the same method actually has the effect of listening for notification execution

__block modifies a variable after it has been captured by a block

  • The first layer copies the object itself from the stack to the heap. Because it’s modified by a block, you have a structure like Blockbyref in the middle that has a member variable with relevant information
  • Layer 2: because it is modified by block, there is a structure like Block_byref in the middle, which contains the relevant information of the member variables!
struct Block_byref {
	void *__ptrauth_objc_isa_pointer isa: 
    struct Block_byref *forwarding;
	volatile int32_t flags;// contains ref count 
    uint32_t size;
};

struct Block_byref_2 {
	// requires BLOCK_BYREF_HAS_COPY_DISPOSE
	BlockByrefKeepFunction byref_keep; //=__Block_byref_id_object_copy_131
	BlockByrefDestroyFunction byref_destroy; // =___Block_byref_id_object_dispose_131
}

struct Block byref 3 {
	// requires BLOCK_BYREF_LAYOUT_EXTENDED 
    const char *layout;
}
Copy the code

The BlockByrefKeepFunction byref_keep function is used for object_copy, and the Block_object_assign function is used for copying external objects.

  • Third layer: outsideBlock_byrefAfter the Block gets caught, it assigns a copy! throughBlock_object_assignFunction call:*dest=_Block_byref_copy(object); Launch ofBlock_byrefA copy of the