One: About timers

CADisplayLink, NSTimer, and GCD can all implement the function of timer, but you need to pay attention to the problem of circular reference. Note: CADisplayLink and NSTimer both rely on runLoop to implement them. Because of this, NSTimer may not be on time if the runLoop is too heavy

CADisplayLink

self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTest)];
    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
Copy the code

Resolving circular references

Using intermediate classes, using message forwarding mechanism to process

@interface MJProxy1 : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end


#import "MJProxy1.h"
@implementation MJProxy1
+ (instancetype)proxyWithTarget:(id)target
{
    MJProxy1 *proxy = [[MJProxy1 alloc] init];
    proxy.target = target;
    return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.target;
}
@end
Copy the code

Find that one in the controller and call it like this

self.link = [CADisplayLink displayLinkWithTarget:[MJProxy proxyWithTarget:self] selector:@selector(linkTest)];
    [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
Copy the code

NSTimer

The following method, NSTimer, makes a strong reference to target, or a circular reference if the controller also declares a strong timer

[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES];
Copy the code

What if we declare a weakSelf self to substitute in? The problem of circular reference still cannot be solved. This method can only be applied inside the block, where weakSelf is only a parameter, and in fact self is still found. There may be a parameter inside the timer that strongly references target.

__weak typeof(self) weakSelf = self;
Copy the code

Solve circular reference scheme 1

In fact, we can use the following 👇 API to create timers, where weakSelf can solve the problem of circular reference

[NSTimer scheduledTimerWithTimeInterval: 1 repeats: YES block: ^ (NSTimer * _Nonnull timer) {NSLog (@ "timer, the content of the need to perform");}].Copy the code

Solution for circular reference scheme 2

Using intermediate classes, using message forwarding mechanism to process

@interface MJProxy1 : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end


#import "MJProxy1.h"
@implementation MJProxy1
+ (instancetype)proxyWithTarget:(id)target
{
    MJProxy1 *proxy = [[MJProxy1 alloc] init];
    proxy.target = target;
    return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.target;
}
@end
Copy the code

Find that one in the controller and call it like this

The self. The timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: [MJProxy proxyWithTarget: self] selector:@selector(timerTest) userInfo:nil repeats:YES];Copy the code

Solve circular reference solution 3

NSProxy is used for message forwarding

@interface MJProxy : NSProxy + (instancetype)proxyWithTarget:(id)target; @property (weak, nonatomic) id target; @implementation MJProxy + (instancetype)proxyWithTarget:(id)target {// NSProxy object does not need to call init, Because it doesn't have init method MJProxy *proxy = [MJProxy alloc]; proxy.target = target; return proxy; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.target methodSignatureForSelector:sel]; } - (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:self.target]; } @endCopy the code

Call from controller (note: CADisplayLink is also available)

The self. The timer = [NSTimer scheduledTimerWithTimeInterval: 1.0 target: [MJProxy proxyWithTarget: self] selector:@selector(timerTest) userInfo:nil repeats:YES];Copy the code

The GCD timer

Note:

  1. The COMMUNIST party doesn’t need us to destroy them
  2. Can be called in child threads

GCD timer encapsulation

#import <Foundation/Foundation.h> @interface WYTimer : NSObject + (NSString *)execTask:(void(^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async; + (NSString *)execTask:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async; + (void)cancelTask:(NSString *)name; #import "WYTimer.h" @implementation WYTimer static NSMutableDictionary *timers_; dispatch_semaphore_t semaphore_; + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ timers_ = [NSMutableDictionary dictionary]; semaphore_ = dispatch_semaphore_create(1); }); } + (NSString *)execTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval repeats:(BOOL)repeats async:(BOOL)async { if (! task || start < 0 || (interval <= 0 && repeats)) return nil; Dispatch_queue_t queue = async? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue(); Dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); Dispatch_source_set_timer (timer, dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0); dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER); // Unique identifier of timer NSString *name = [NSString stringWithFormat:@"%zd", timers_.count]; Timers_ [name] = timer; dispatch_semaphore_signal(semaphore_); // Set the callback dispatch_source_set_event_handler(timer, ^{task(); if (! Repeats) {// Unrepeatable task [self cancelTask:name]; }}); // Start timer dispatch_resume(timer); return name; } + (NSString *)execTask:(id)target selector:(SEL)selector start:(NSTimeInterval)start interval:(NSTimeInterval)interval  repeats:(BOOL)repeats async:(BOOL)async { if (! target || ! selector) return nil; return [self execTask:^{ if ([target respondsToSelector:selector]) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [target performSelector:selector]; #pragma clang diagnostic pop } } start:start interval:interval repeats:repeats async:async]; } + (void)cancelTask:(NSString *)name { if (name.length == 0) return; dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER); dispatch_source_t timer = timers_[name]; if (timer) { dispatch_source_cancel(timer); [timers_ removeObjectForKey:name]; } dispatch_semaphore_signal(semaphore_); } @endCopy the code

Call in controller

// Self. task = [MJTimer execTask:self selector:@selector(doTask) start:2.0 interval:1.0 repeats:YES async:NO];Copy the code

IOS Memory Layout

Tagged Pointer

  1. Starting from 64-bit, iOS introduces Tagged Pointer technology to optimize storage of small objects such as NSNumber, NSDate, and NSString

  2. Before Tagged Pointer is used, objects such as NSNumber need to dynamically allocate memory and maintain reference counting. The NSNumber Pointer stores the address value of the NSNumber object in the heap

  3. After using Tagged Pointer, the Data stored in the NSNumber Pointer becomes Tag + Data, that is, the Data is stored directly in the Pointer

  4. When Pointers are insufficient to store data, dynamically allocated memory is used to store data

  5. Objc_msgSend can recognize Tagged Pointer, such as the intValue method of NSNumber, and directly extract data from Pointer, saving previous call overhead

How do I check whether a Pointer is Tagged Pointer? For iOS, the highest significant bit is 1 (64bit). For Mac, the lowest significant bit is 1

The interview questions

What are the results of the following two pieces of code?

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"abcdefghijk"];
        });
    }
    
    
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    for (int i = 0; i < 1000; i++) {
        dispatch_async(queue, ^{
            self.name = [NSString stringWithFormat:@"abc"];
        });
    }
Copy the code

First segment crashed, bad memory access second segment no problem

Self.name = [NSString stringWithFormat:@” ABC “] this code will call the setName method, and multiple threads will execute this code, repeatedly releasing when calling release, resulting in bad memory access.

- (void)setName:(NSString *)name
{
    if (_name != name) {
        [_name release];
        _name = [name retain];
    }
}
Copy the code

Self. name = [NSString stringWithFormat:@” ABC”

[NSString stringWithFormat:@” ABC “] [NSString stringWithFormat:@” ABC “] [NSString stringWithFormat:@” ABC “]] [NSString stringWithFormat:@” ABC “]

Memory management of OC objects

1. In iOS, reference counting is used to manage the memory of OC objects

2. The default reference count of a newly created OC object is 1. When the reference count is reduced to 0, the OC object is destroyed to free its memory

3. A call to retain will give the OC +1 reference count and a call to release will give the OC -1 reference count

Memory management lessons learned When you call the alloc, new, copy, and mutableCopy methods and you return an object, and when you don’t need that object, you call release or autoRelease torelease it and you want to own that object, you make its reference count +1; If you don’t want to own an object, let its reference count be -1

Extern void _objc_autoreleasePoolPrint(void) extern void _objc_autoreleasePoolPrint

Copy and mutableCopy

About the Copy of custom objects

To copy a custom object, follow the NSCopying protocol and implement the copyWithZone method

-(id)copyWithZone:(NSZone *)zone
{
    
}
Copy the code

Reference counter

In 64bit, reference counts can be stored directly in an optimized ISA pointer, in extra_RC in a bitwise domain, or possibly in the SideTable class.

Refcnts are hash tables that hold object reference counts

Weak implementation principle

Weak and unsafe_unretain Neither has a strong reference, but weak objects are automatically set to nil when destroyed, while unsafe_unretain does not empty objects.

Note: ARC combines LLVM + Runtime for automatic code insertion

Principle of automatic release pool

1. The main underlying data structures of the automatic release pool are __AtAutoreleasePool and AutoreleasePoolPage 2. Objects that call AutoRelease are ultimately managed through the AutoreleasePoolPage object

Objc_autoreleasePoolPush is called to create the pool and objc_autoreleasePoolPop is called to destroy the pool

@autoreleasepool {
//        atautoreleasepoolobj = objc_autoreleasePoolPush();
        
       MJPerson *person = [[[MJPerson alloc] init] autorelease];
        
//        objc_autoreleasePoolPop(atautoreleasepoolobj);
    }
Copy the code

AutoreleasePoolPage structure

1. Each AutoreleasePoolPage object occupies 4096 bytes of memory, which is used to store its internal member variables. 2. All AutoreleasePoolPage objects are linked together in a bidirectional linked list

1. Calling the push method pushes a POOL_BOUNDARY onto the stack and returns its memory address

2. A POOL_BOUNDARY memory address is passed in when the POP method is called. A release message will be sent from the last object pushed until the POOL_BOUNDARY is encountered

3. Id *next points to the next area where the address of the AutoRelease object can be stored

When does an autoRelease object get called release

In this case, the object is released when page calls objc_autoreleasePoolPop at the end of the brace

@autoreleasepool {

 MJPerson *person = [[[MJPerson alloc] init] autorelease];

    }
Copy the code

If this is the case, the object is not destroyed when the braces end.

- (void)viewDidLoad
{
    [super viewDidLoad];
    MJPerson *person = [[[MJPerson alloc] init] autorelease];
}
Copy the code

Here’s why:

The release of this object is controlled by Runloop, which may call RealEase while Runloop is asleep in the Runloop. If the above code is in MRC, it will be released immediately if ARC inserts RealEase