IOS interview questions

This interview is entitled personal use version, such as the subsequent spread out, please forward friends must be annotated, the correctness of the answer is questionable, my answer does not represent the authority, just personal understanding. There is confusion inside the article, so live with it. In addition, most images cannot be loaded, and the MARKDown format is not uniform (different platforms), due to the lazy bloggers do not want to change, but it does not affect the final effect.

Update log

  • August 17, 2020 Updated with a new extension of article 23, regarding root causes of NSTimer circular references, and optimization options

First, hard technology

1. What is the difference between object methods and class methods?

  • Object methods can access member variables individually.
  • An object method cannot be called directly from a class method. To call an object method, you must create or pass in an object.
  • Class methods can have the same name as object methods.

1. What happens if self is called in a class method?

  • Self cannot call a Class method in an instance method; self is not Class.
  • Within a class method self can call other class methods.
  • Self cannot call instance methods in class methods.
  • Summary: Self in a class method is the first address of an object in a class/ instance method.

2. Talk about the composition of objects, class objects, metaclasses, and metaclasses and how they relate to each other.

  • The isa pointer points to the class object and member variables are stored in the structure of the object
  • The isa pointer to a class object points to a metaclass, and the ISA pointer to a metaclass points to NSObject’s metaclass
  • Structures for class objects and metaclasses include ISA, superClass, cache, and so on

3. Why is the object method not stored in the object structure, but in the structure of the class object?

  • Methods can be shared by each object. If each object stores a list of methods, it would be a waste of memory. Because the ISA of the object points to the class object, when it is called, it can be directly searched in the class object, saving a lot of memory space.

Extension 4. Where do class methods exist? Why do metaclasses exist?

  • All classes are themselves an object to which we can send messages (that is, invoke class methods).

In order to call class methods, the isa pointer to that class must point to an objC_class structure that contains those class methods. This leads to the concept of meta-class, where all the information needed to create class objects and class methods is held.

Extension 5. What is a wild pointer?

  • A wild pointer refers to an object that has been freed or reclaimed, but the pointer to that object is not modified so that it points to the reclaimed memory address.
  • There is no problem accessing wild pointer, using wild pointer will appear Crash! A sample of
  __unsafe_unretained UIView *testObj = [[UIView alloc] init];
   NSLog(@"testObj pointer to address :%p Pointer to address :%p", testObj, &testObj);
   [testObj setNeedsLayout];
   [testObj setNeedsLayout]; [testObj setNeedsLayout]; Will flash back
Copy the code

Extended 6. How to detect wild Pointers?

This is a net friend summary, interested can look at: www.jianshu.com/p/9fd4dc046… Oneself, also see joy, its principle what of, have different opinions. Development is too hard!

7. What are the causes of Crash?

2, KVC crash 3, EXC_BAD_ACCESS 4, KVO crash 5, set class related crash 6, multithreaded crash 8. Crash caused by Watch Dog timeout 9. Crash caused by background return NSNull, which is commonly seen in Java as the background server development language

8. Without using a third party, how to know the crash problem of the App that has been online, and which method to which class?

The general implementation is as follows.

  • Flash back can use NSSetUncaughtExceptionHandler statistical information.
  • The statistical information is sent to the background in the form of data by network request
  • Collect information in the background and conduct troubleshooting
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        NSSetUncaughtExceptionHandler(&my_uncaught_exception_handler);
        return YES;
    }

    static void my_uncaught_exception_handler (NSException *exception) {
        // You can get the NSException
        NSLog(@ "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");
        NSLog(@ "% @",exception);
        NSLog(@ "% @",exception.callStackReturnAddresses);
        NSLog(@ "% @",exception.callStackSymbols);
        NSLog(@ "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *");
    }
Copy the code

Implementation such as: blog.csdn.net/u013896628/…

How many ways to introspect in iOS?

  • IsMemberOfClass // Whether the object is of a certain type
  • IsKindOfClass // Whether the object is an object of a type or a subclass of a type
  • IsSubclassOfClass // Whether a class object is a subclass of another type
  • IsAncestorOfObject // Whether a class object is the parent of another type
  • RespondsToSelector // Whether a method can be responded to
  • ConformsToProtocol // Specifies whether to comply with a protocol

==, isEqualToString, isEqual?

  • ==, comparing the values of the two Pointers (whether the memory address is the same).
  • IsEqualToString, which compares whether two strings are equal.
  • IsEqual determines whether two objects are the same in type and value.

3. What is the difference between the class method and object_getClass method?

  • The instance class method returns object_getClass(self) directly
  • The class class returns self directly
  • Object_getClass, on the other hand, returns a metaclass

3. Deep copy and shallow copy

  • Depth refers to whether a new object is created (opening up a new memory address) or just copying Pointers.
  • Copy and mutableCopy are mutable and immutable, so everything involving copy becomes immutable and mutableCopy becomes mutable.
  • MutableCopy is a deep copy.
  • The copy operation is immutable for shallow copy and mutable for deep assignment.

4. Why does NSString use copy?

  • The main thing is to prevent NSString from being modified, or Strong if it’s not being modified.
  • When the NSString assignment source is NSString, strong and copy have the same effect.
  • When the source of an NSString assignment is NSMutableString, copy will make a deep copy and create a new object, so changing the source of the assignment doesn’t affect the value of the NSString.

5. What actually happens in iOS when blocks capture external local variables? What’s going on in __block?

  • A block captures the instantaneous value of an external local variable that is currently executing inside the block. A look at the C++ source code shows that its internal code is captured at the same time

  • In fact, the underlying block generates an attribute value with the same name as the external variable. If the internal value is changed, it actually changes the value before it is captured. The code only captures the internal value once, and does not capture it again, so the value cannot be changed inside the block.

  • If the object type is currently captured, a pointer to the memory address of the current object (heap) can be recreated inside the block. All internal operations are controlled by the same memory address, so you can modify the properties of the current internal object. However, you cannot directly modify the current pointer (you cannot directly modify the contents of the stack) (that is, regenerate a new memory address). The principle is the same as capturing basic data types.

  • In plain English, inside a block you can modify what’s in the heap, but not anything in the stack directly.


  • If the __block attribute creates a “copy” of an external variable at run time, the stack memory address is placed in the heap and the value of the external variable can be changed within the block.

6. Why do iOS blocks use copy?

  • A block is an object
  • In MRC, when a block is created, its memory is so weird that it has to be allocated on the stack rather than on the traditional heap, and its function is created at the time it is created (see “dead”). If called outside of the function at creation time, it will crash.
  • So you use copy to copy what’s on the stack to the heap, and you save it.
  • ** the stack block is no longer visible in ARC. Strong is just as arbitrary as copy, and copy follows its tradition, **

7. Why can’t a Property be created in a class (except runtime)?

  • Classification is implemented by placing the category method, attribute, and protocol data in the category_T structure, and then copying the list of methods in the category_T structure into the method list of the class object. Categories can add attributes, but they do not automatically generate member variables and set/ GET methods. There are no member variables in the CATEGORY_t structure. We know from our previous analysis of the object that member variables are stored in the instance object and are determined at compile time. Categories are loaded at run time. Then we cannot add the member variables of the class to the structure of the instance object at runtime. Therefore, no member variables can be added to a classification.

  • The deeper answer is that the location of the class in memory is determined at compile time, and any subsequent changes to the code will not change the location in memory. The class_ro_t property cannot be changed at runtime. Add methods that modify class_rw_t methods instead of class_ro_t baseMethods

Extension: The principle of associated objects?
  • The associated object is not stored in the memory of the associated object itself, but stored in a globally unified container.
  • Managed by AssociationsManager and stored in a singleton Hash table AssociationsHashMap that it maintains;
  • Thread safety is ensured using the AssociationsManagerLock spinlock
Extension: What can be added to categorization?
  • Instance methods, class methods, protocols, properties
Extension: How does Category work?
  • Categories are separated from their original classes when they are first compiled, and only merged by the Runtime when the program is running.
Do we need to release objects associated with the Runtime Associate method when the main object is dealloc?
  • No, the associated objects are released much later in their life than the objects themselves, in object_Dispose () method called by nsobject-dealloc.
Can extensions add instance variables to compiled classes and power variables to classes created at run time?
  • You cannot add instance variables to a compiled class. Because the compiled class is registered with the Runtime, the linked list of objC_iVAR_list instance variables and the memory size of the objC_iVAR_list instance variables in the class structure are determined, so instance variables cannot be added to existing classes
  • Ability to add strength variables to classes created at run time. Call the class_addIvar function
If foo is executed where it is executed, will the foo of the main class be overwritten? If you want to execute only the foo method of the main class, how do you do it?
  • The method of the main class is overridden by the method of foo of the main class. The method of the main class is overridden by the method of Foo of the main class. The method of the main class is overridden by the method of foo of the main class.
  • If you want to execute only the methods of the main class, you can traverse the list of methods in reverse order, and the foo method that you first traverse is the method of the main class
- (void) foo {[class invokeOriginalMethod:self selector:_cmd];
}

+ (void)invokeOriginalMethod:(id)target selector:(SEL)selector {
    uint count;
    Method *list = class_copyMethodList([target class], &count);
    for ( int i = count - 1 ; i >= 0; i--) {
        Method method = list[i];
        SEL name = method_getName(method);
        IMP imp = method_getImplementation(method);
        if (name == selector) {
            ((void(*) (id, SEL))imp)(target, name);
            break;
        }
    }
    free(list);
}
Copy the code

8. The call of load and Initilze, and the call order of subclasses?

The +load method is called when the Runtime loads classes and classes (whether or not these classes are used, the program is loaded into memory and the +load method is called). The +load of each class or category is called only once during the program execution (unless manually called by the developer).

② Call method: the system automatically calls +load directly through the function address call, the developer manually calls +load message mechanism objc_msgSend function call.

③ Call order: call the +load of the class first, according to the order of compilation (compile first, call first), call the +load of the parent class before calling the +load of the subclass; The +load of the class is then called in compile order (compile first, call first) (note: other methods of the class are: post-compile, call first).


The +initialize method is called when the class first receives a message. If a subclass does not implement the +initialize method, the +initialize method of the parent class will be called. Therefore, the +initialize method of the parent class may be called more than once, but it does not mean that the parent class will be initialized more than once.

② Call method: message mechanism objc_msgSend function call.

Call the parent class +initialize, and then call the child class +initialize.

  • The +initialize method is called as a message mechanism, rather than directly from a function address like +load.

9. What is thread safety?

  • Multiple threads accessing a piece of code at the same time does not cause data clutter

10. Which of the projects you’ve worked on have used thread safety?

Answer: for example, 12306 the same train ticket, the same time period many people grab tickets! How to solve the mutex usage format

Synchronized {// 需要锁定的代码  }Note: Locking1Part of the code is used only1Multiple locks will not work. Advantages and disadvantages of mutex Advantages of mutex: It effectively prevents data security problems caused by multi-thread resource snatches Disadvantages: It consumes a large amount of CPU resources Prerequisites for mutex: Multiple threads seize the same resource. Thread synchronization, where multiple threads execute task mutex sequentially, is a thread synchronization technique that uses the atomic and non-atomic properties OC in Objective-C when defining attributesnonatomicAtomic: atomic property, forsetter/getterMethods are locked (atomic by default)nonatomicNon-atomic attributes, unlockedassign, atomic) int age;
 - (void)setAge:(int)age
{ 
    @synchronized(self) { _age = age; }} - (int) age {int age1 = 0;
	@synchronized(self) { age1 = _age; }} Selection of atomic and non-atomic attributesnonatomicAtomic: Thread-safe, consumes a lot of resourcesnonatomic: Non-thread-safe, suitable for mobile devices with small memory iOS development recommendations all properties are declared asnonatomicTry to avoid multi-threading to grab the same resource try to lock, resource snatching business logic to the server side to deal with, reduce the pressure of mobile client atomic can ensure thread safety? No, a deeper locking mechanism is needed, because if a thread reads a property multiple times in a row, other threads are rewriting the value at the same time, and still get different property values! Or if one thread fetches the value of the current property, another thread releases the property, possibly causing a crashCopy the code

11. Have you implemented the singleton pattern? How many implementations can you use?

1.Using the GCD: import"Manager.h"
implementation Manager
+ (Manager *)sharedManager {
  static dispatch_once_t onceToken;
  static Manager * sharedManager;
  dispatch_once(&onceToken, ^{
    sharedManager=[[Manager alloc] init];
  });
  returnsharedManager; } end note:dispatch_onceThis function ensures that a piece of code is executed only once in the entire application life cycle!2.Ways not to use GCD:static Manager *manager;
implementation Manager
+ (Manager *)defaultManager {
    if(! manager) manager=[[self allocWithZone:NULL] init];
    return  manager;
}
end

3.Normal full version +(id)shareInstance{
     static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
      if(_instance == nil)
            _instance = [MyClass alloc] init]; 
    });
     return _instance;
}

// Re-write allocWithZone, as long as it is the same as method 1 and method 2.+ (id)allocWithZone:(struct _NSZone *)zone{
   return [self shareInstance];
} 

// Ensure that copy is the same- (id)copyWithZone:(NSZone *)zone{  
    return _instance;  
} 
// The purpose of method 3 is to create a way for developers to use alloc or copy instead of shareInstance when calling a singleton to create inconsistencies in the singleton

//   
Copy the code

1. How are singletons destroyed?

// Static dispatch_once_t onceToken; This goes out of the function and becomes global.
+ (void)attempDealloc {
    onceToken = 0; // If set to 0,GCD will assume that it has never been executed. It defaults to 0 so that the next time shareInstance is called, the object is created again.
    _sharedInstance = nil;
}

dispatch_once_tThe way it works is,staticThe modifier will initialize it by default0If and only if it is0whendispatch_once(&onceToken, ^{}) this function can only be called if this function is executeddispatch_once_tStatic becomes -1It will never be calledCopy the code

2. How to implement singleton without dispatch_once


1.First, rewrite the +allocWithZone: method; + (instancetype)allocWithZone:(struct _NSZone *)zone {
    static id instance = nil;
    @synchronized (self) { / / the mutex
        if (instance == nil) {
            instance = [superallocWithZone:zone]; }}return instance;
}

2.The second way, instead of rewriting the +allocWithZone: method, is to use it directly@synchronizedTo ensure thread-safety, the other method is the same as above; + (instancetype)sharedSingleton {
    static id instance = nil;
    @synchronized (self) {
        if(! instance) { instance = [[selfalloc] init]; }}return instance;
}
Copy the code

12. What have you done with singletons during project development?

A: When the entire application shares one resource, for example:

  • Example Set the configuration information for the singleton class to access the application
  • The user’s personal information is stored in NSUserDefaults after login, and the login class is further encapsulated by singleton to facilitate global access
  • Prevents a singleton pair from applying multiple pairs to a local data store

13. Basic principles of APNS

  • basic
  • Phase one: The server side of the application packages the message to be sent, along with the identity of the destination iPhone, and sends it to APNS.
  • Phase 2: APNS looks for iphones with corresponding logos in its list of iphones registered with Push service and sends the message to iPhone.
  • Phase 3: The iPhone delivers the incoming message to the appropriate application and pops up a Push notification.
  • Detailed instructions

The first is registration

  • Device Connects to the APNs server and carries the Device serial number (UUID)
  • The connection is successful, APNs is packaged and processed to generate deviceToken and returned to the registered Device.
  • The Device sends the obtained DeviceToken to our own application server
  • Complete the registration of the devices that need to be pushed on the APNs server and our own application server

Push the process

  • 1. First of all, if the current APP is installed on the phone and the network is maintained, the APNs server will verify deviceToken, and the successful one will be in a long connection. (There will be an interview question? If the app has been successfully registered, downloaded and agreed to open the push function, can you still accept push after deleting the app?)
  • 2. When we push messages, our server packages them according to the specified format and sends them to the APNs server with DeviceToken.
  • 3. The APNs server pushes the new message to the iOS device, and then displays the pushed message on the device screen.
  • 4. When iOS devices receive a push message, they notify our app and give us a reminder

// The push process is shown below

14. RunLoop basics

  • What are the RunLoop modes?

A: There are five RunLoop modes in iOS

NSDefaultRunLoopMode (default mode, blocks old events when there is an event response) NSRunLoopCommonModes (normal mode, UITrackingRunLoopMode does not affect any events. There are also two system level modes: one is executed once the app is started and the other is the mode in which the system detects various events of the appCopy the code
  • The basic execution principle of RunLoop

A: The system already has a Runloop that detects actions or events within the App. When an input source (a user’s direct or indirect action) has an “action”, the runloop listens for the status of the input source and then performs some corresponding actions within the system. When it’s done, it goes back to sleep and waits for the next time it wakes up,

  • RunLoop and thread relationship

  • The purpose of RunLoop is to manage threads. When RunLoop is enabled, threads go to sleep after executing tasks, waiting to receive new tasks, rather than quitting.

  • Why is only the main thread runloop open

  • Once the program is started, it should always run, never quit. Basically, to keep the program from dying


How to ensure that a thread never dies (resident thread)

	// Create a thread for testing
	 NSThread *thread = [[NSThread alloc]  initWithTarget:self selector:@selector(play) object:nil];
    [thread start];

    // A thread is guaranteed to never die
    [[NSRunLoop currentRunLoop] addPort:[NSPort port] -forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
    
    
    // Handle thread event handling where appropriate
    [self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:NO];
    
Copy the code

15. Weak attributes?

1. What do you understand about weak attributes?Copy the code
1.implementationweakAfter, why does the object automatically become freenil? The Runtime does the layout for registered classesweakObjects are put into a hash table. withweakThe memory address of the object pointed to as the key, Value isweakAn array of addresses for Pointers. When released, all of them are found internally by the current keyweakAnd then we go through that array and set the data in that array tonil. Internally, the underlying source code is also associated with the current object as SideTable, which has three internal properties, one is a spin lock, one is reference counter related, and one is maintenanceweakLife is a structure like the **SideTable**, which can be looked at for half an hour.Copy the code

extension

  • What happens when you send a message to a nil object in objC?

The first thing to look for an object isa pointer is that the 0 address is returned, so there is no error, no error

  • What happens when OBJC sends a message to an object?
- The first is to find the corresponding isa pointer through objclass- First go to the operation object in the cache method list objc_cache to find the current method, if found directly implement IMP - if not found in the cache, then inclassIf foo is used in the Method listclass- If foo is not found in the superClass, it implements a list of IMP cache methods for Foo. That is, every time this method is executed, it does so much work that it is too expensive to perform. So an objc_cache, Method_name as the key, method_IMP as the Value, and when the next message is received, the objc_cache is used to find the corresponding IMP of Foo. Avoid going through objc_method_list every time. If no method is found, a special message forwarding mechanism is used, which looks like this// Dynamic method parsing and forwardingIn the above example, if foo is never found, normally an error will occur, but the OC runtime gives us three chances to remedy the error before it is found1.The Runtime sends either +resolveInstanceMethod: or +resolveClassMethod: attempt to resolve;2.If the resolve method returnsNO, the Runtime will send - forwardingTargetForSelector: allows you to forward this message to another object.3.If there is no new target object returns, the Runtime will send - methodSignatureForSelector: and - forwardInvocation: message. You can send - invokeWithTarget: messages to manually forward or send - doesNotRecognizeSelector: throw an exception.Copy the code

16. What is the relationship between UIView and CALayer?

- The most obvious difference between the two is that the View can accept and handle events, while the Layer can't; - Each UIView has a CALayer behind it to draw and display content, and UIView size styles are provided by the inner Layer. Both have tree hierarchies, with SubLayers inside the layer and SubViews inside the View. But Layer has an extra AnchorPoint than the View - when the View is displayed, UIView acts as a Layer's CALayerDelegate, the View is displayed by the display of the inner CALayer and the CALayer is modified by default to support implicit animation, so when you animate the Layer of UIView, View acts as a proxy for Layer, Layer through actionForLayer:forKey: request corresponding action(animation behavior) to View - Layer internal maintenance of three Layer tree, respectively presentLayer Tree(animation tree),modeLayer Tree(model Tree), Render Tree(Render Tree), when doing iOS animation, we modify the properties of animation, in fact, the animation is Layer presentLayer property value, and the final display on the interface is actually to provide View modelLayerCopy the code

16. What does @synthesize and @dynamic do, respectively

- at sign property has two words, at sign synthesize, and at sign dynamic. If at sign syntheszie and at sign dynamic don't write, then the default is @syntheszie var = _var; The semantics of the -@ synthesize is that if you don't implement setter and getter methods manually, then the compiler automatically adds them for you. - @dynamic tells the compiler that setter and getter methods for a property are implemented by the user and not automatically generated. (Of course, you only need to provide getters for readonly properties). If a property is declared as @dynamic var, and you don't provide @setter and @getter methods, that's fine at compile time, but when the program runs to instance.var = someVar, the lack of setter methods will crash the program; Or when someVar = var is run, it will also crash due to the lack of getter methods. Compile time is fine, and the corresponding method is executed at run time, which is called dynamic binding.Copy the code

17. What does static do?

The static keyword modifies functions and variables as follows: ** Class methods are not allowed to access instance variables (functions). Instance variables (functions) that are static (functions) are not allowed to access instance variables (functions). Can be accessed by class methods; ** persistent ** static modified variables that can and can only be initialized once; ** Default initialization ** static modified variables, default initialization to 0;Copy the code

18. What happens when OBJC sends a message to an object?

- objc_msgSend(recicver, selecter..)
Copy the code

19. What is runloop for? What does runloop have to do with threads? Does the main thread have Runloop enabled by default? What about child threads?

Runloops correspond to threads one by one. A runloop corresponds to a core thread, which is called core because runloops can be nested, but there can only be one core. Their relationship is stored in a global dictionary. 2. The runloop is used to manage threads. When the runloop is enabled, threads will go to sleep after executing tasks. The runloop is created on the first fetch and destroyed at the end of the thread. 3. For the main thread, the runloop is created by default as soon as the program starts. 4. For child threads, runloops are lazily loaded and only created when we use them, so be careful when using child timers: make sure the runloop is enabled for the child thread, otherwise the timer will not call back.Copy the code

20. How do I manually trigger the KVO of a value

Key-value observation notifications depend on two methods of NSObject: willChangeValueForKey: and didChangevlueForKey:. Before an observed property changes, willChangeValueForKey: must be called, which records the old value. When change happens, didChangeValueForKey: is called, which in turn observeValueForKey: ofObject: change: context: will be invoked. If you could implement these calls manually, you would be “manually triggered”.

How to set screening conditions for system KVO?

  • For example, disable the default KVO for the age attribute of the Person class. If age is greater than 18, KVO is triggered manually
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key {
    if ([key isEqualToString:@"age"]) {
        return NO;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}

- (void)setAge:(NSInteger)age {
    if (age >= 18) {[self willChangeValueForKey:@"age"];
        _age = age;
        [self didChangeValueForKey:@"age"];
    }else{ _age = age; }}Copy the code

1. Does modifying attributes via KVC trigger KVO? How about modifying a member variable directly?

  • KVO is triggered. Even if there is no declaration attributes, only the member variables, as long as accessInstanceVariablesDirectly returns YES, allowing access to the member variables, so regardless of calling a setter method, through the KVC changes the value of the member variables can trigger KVO. This also indicates that the willChangeValueForKey: method and didChangeValueForKey: method are implemented internally through KVC
  • Modifying member variables directly does not trigger KVO. Modifying a member variable directly does nothing inside it but simply assigns, so it does not trigger.

The underlying implementation of KVC?

  • Principle of assignment method setValue:forKey:

(1) First, setKey: method and _setKey: method will be searched in sequence. As long as either of these two methods is found, the parameter will be passed directly and the method will be called.

(2) if not found the setKey: and _setKey: method, so this time will see accessInstanceVariablesDirectly method return value, if the return is NO (that is, does not allow direct access to the member variables), The setValue:forUndefineKey: method is called and “NSUnknownKeyException” is thrown;

(3) if the accessInstanceVariablesDirectly method returns YES, that is to say, you can access the member variable, it can according to the sequence to find _key, _isKey, key, isKey these four member variable, if looked up, direct assignment; If it still doesn’t, the setValue:forUndefineKey: method is called and “NSUnknownKeyException” is thrown.

  • Value method valueForKey: principle

(1) First of all, getKey:, key, isKey, _key: will be searched in order of the four methods, as long as any one of the four methods is found, the method will be directly called;

(2) if not found, then the time will see accessInstanceVariablesDirectly method return value, if the return is NO (that is, does not allow direct access to the member variables), then invokes the valueforUndefineKey: method, And throws an exception “NSUnknownKeyException”.

(3) if the accessInstanceVariablesDirectly method returns YES, that is to say, you can access the member variable, it can according to the sequence to find _key, _isKey, key, isKey these four member variable, if found, the value directly; If the member variable is still not found, the valueforUndefineKey method is called and an exception “NSUnknownKeyException” is thrown.

21. ViewController lifecycle

In order of execution:1.InitWithCoder: Triggered when initialized through a NIB file.2.AwakeFromNib: When the NIB file is loaded, an awakeFromNib message is sent to each object in the NIB file.3.LoadView: Starts loading the view of the view controller.4.ViewDidLoad: The view controller's view is loaded.5.ViewWillAppear: The view controller's view will be displayed on the window.6.UpdateViewConstraints: The view of the view controller starts updating the AutoLayout constraint.7.ViewWillLayoutSubviews: The view controller's view will update the position of the content view.8.ViewDidLayoutSubviews: The view controller's view has updated the position of the view.9.ViewDidAppear: The view controller's view is displayed on the window.10.ViewWillDisappear: The view controller's view will disappear from the window.11.ViewDidDisappear: The view controller's view disappears from the window.Copy the code

22. Network protocols

  • TCP three handshakes and four waves?

Three-way handshake

** The client sends a SYN packet with SYN=1 and SEq = X to the server, and the client enters the SYN_SENT state

** The server replies with SYN=1, SEq = Y,ACK=1, ACK= X +1, and the server enters the SYN_RCVD state.** After receiving the acknowledgement packet, the client sends an acknowledgement packet to the server with ACK=1. Ack =y+1, the client enters ESTABLISHED. After receiving the confirmation packet from the client, the server also enters ESTABLISHED. In this case, the link is successfully created

- ah! - Yeah. - Here you goCopy the code

Why the three-way handshake is required: An error occurs in case a connection request segment that has expired is suddenly sent to the server. Suppose this is an invalid connection request segment. After receiving the invalid connection request segment, the server mistakenly considers it a new connection request sent by the client. Then the client sends a confirmation message to agree to establish a connection. Assuming that the “three-way handshake” is not used, a new connection is established as soon as the server sends an acknowledgement. Since the client does not send a connection request, it ignores the server’s confirmation and does not send data to the server. However, the server assumes that the new transport connection has been established and waits for data from the client. As a result, many of the server’s resources are wasted.

Four times to wave

** When the server receives the request to close the link, it sends a reply to the client, I know, and then stops receiving data.** 3. When the server is finished sending data, it sends a close link to the client, 4.** When the client receives a request to close the link, it sends a response to the server, I know, and then stops receiving data

- ah! - Yeah. - Off. - OkayCopy the code

Why is it necessary to wave four times: Because TCP is full-duplex communication, it may send data to the client when it receives the closing request from the client. Therefore, it cannot respond to the closing request from the client, and sends the closing request at the same time

extended

  1. What’s the difference between HTTP and HTTPS?

    • HTTP is a network protocol that uses plaintext data transmission.
    • HTTPS is an upgrade of HTTP, which adds data encryption on the basis of HTTP. The data is encrypted before it is transmitted and then sent to the server. This way, even if the data is intercepted by a third party, your personal information is still safe because the data is encrypted. This is the big difference between HTTP and HTTPS.
  2. HTTPS encryption mode?

    • Https uses a combination of symmetric encryption and asymmetric encryption to communicate.

    • Https is not a new protocol at the application layer, but the Http communication interface uses SSL and TLS to enhance encryption and authentication mechanisms.

      • Symmetric encryption: Encryption and decryption are the same key
      • Asymmetric encryption: Key acceptance is classified into public key and private key. Public key encryption requires private key decryption, and private key encryption requires public key decryption

How to establish a connection between HTTP and HTTPS?

HTTP

  • After the link is established, the client sends a response to the server
  • The server accepts the request and sends the response to the client
  • The client receives the response and parses it back to the client

HTTPS

  • To use HTTPS, ensure that the server is configured with a correct security certificate
  • The client sends a request to the server
  • The server returns the public key and certificate to the client
  • After receiving the certificate, the client verifies the security of the certificate. If the certificate passes, the client generates a random number, decrypts it with the public key, and sends it to the server
  • After receiving the encrypted random number, the server will use the private key to decrypt it to obtain the real random number, and then use the random number as the private key to encrypt the data to be sent symmetrically.
  • The client receives encrypted data and decrypts the data using the private key (previously generated random value), and parses the data for presentation to the client

The difference between GET and POST in HTTP

  • GET is limited to the length of the URL for certain browsers and servers. But in theory there is no limit

  • POST does not transmit values through urls and is theoretically unlimited.

  • GET concatenates request parameters to the URL, which is not secure,

  • POST puts parameters in the request body, which is relatively safer than GET. However, it is not secure because it can snoop on the data, so encryption is used to make it more secure.

  • GET is faster than POST. Cause: A Post request sends the request to the server for confirmation before it actually sends the data, whereas a Get request sends the request with the data after the link is established. There’s a step missing in the middle. So get is faster than post

  • Post request process

  • The third handshake sends the POST request

  • The server returns a 100 continue response

  • The browser starts sending data

  • The server returns a 200 OK response


  • Get Request process
  • After three handshakes, the third one sends the GET header and data
  • The server returns a 200 OK response

23. Have you ever used performSelector?

  • Have methods been added dynamically
  • So without further ado about the code
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p = [[Person alloc] init];

    // The default person, which does not implement the eat method, can be called via performSelector, but will report an error.
    // Dynamic add methods do not report errors
    [p performSelector:@selector(eat)];
}

@end


@implementation Person

** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** 支那

// void(*)()
// The default method takes two implicit arguments,
void eat(id self,SEL sel)
{
    NSLog(@ % @ % @ "".self.NSStringFromSelector(sel));
}

// When an object calls an unimplemented method, the method is called and the corresponding list of methods is passed.
// This can be used to determine if an unimplemented method is a method that we want to add dynamically
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(eat)) {
        // add the eat method dynamically

        // The first argument: which class to add a method to
        // The second argument: add the method number
        // Add the function implementation of the method (function address)
        V :void @: object ->self: SEL->_cmd
        class_addMethod(self.@selector(eat), eat, "v@:");
    }
    return [super resolveInstanceMethod:sel];
}
@end
Copy the code
  • Of course, you may be asked this question in the interview
// Delay operation and GCD after an effect [p performSelector:@selector(eat) withObject:nil afterDelay:4];Copy the code
  • Did you think it was over? No, the interviewer will probably ask you, *** what does the code above look like in a child thread? Why is that? 支那

    — This method creates an NSTimer timer internally, and this timer is added to the current RunLoop so that no timer methods are executed when the code is placed in the child thread. If you want to execute, start the current thread

[[NSRunLoop currentRunLoop] run];
Copy the code
// Complete call
 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        // [[NSRunLoop currentRunLoop] run]; This is not possible when placed above the runloop, because currently only the runloop is enabled and no events (source, timer, observer) are enabled
         [self performSelector:@selector(test) withObject:nil afterDelay:2];
         [[NSRunLoop currentRunLoop] run];
});

// So I did another test on my own
[self performSelector:@selector(test)]; There is no problem with calling on a child thread.// I tested it again,
 [self performSelector:@selector(test) withObject:nil afterDelay:2]; This method executes the print thread on the main thread1Call the print thread non in the child thread1
Copy the code
  • And then the interviewer starts floating and starts asking you questions about NSTimer? How to do? A:Get him!

NSTimer executes on child threads.

  • NSTimer is not called directly in the child thread. To execute, enable the current Runloop. The specific opening scheme is described above, and is not repeated.

Why is ns Mer not accurate?

  • When the NSTimer trigger time is up, if the runloop is blocked, the trigger time will be postponed to the next runloop cycleThe method code to reduce the error is as follows
// Enable NStimer in child threads, or change the Mode of the current Runloop to NSRunLoopCommonModes
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];

// Use CADisplayLink (iOS devices refresh the screen at a fixed rate, CADisplayLink is normally called at the end of each refresh, with high accuracy)
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(logInfo)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

/ / use the GCD
NSTimeInterval interval = 1.0;
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0.0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_timer(_timer, dispatch_walltime(NULL.0), interval * NSEC_PER_SEC.0);
dispatch_source_set_event_handler(_timer, ^{
    NSLog(@"GCD timer test");
});
dispatch_resume(_timer);
Copy the code

Extended: NStimer circular references?

  • Some people might say, NSTimer itself target refers to self, self refers to Timer and that creates a circular reference, but what if Timer is declared weak? Will circular references be used? Answer: Yes
  • The reason is that NTtimer and Runloop are mutually existing, and I won’t mention any other reasons. Runloop and tmier refer to each other, and Runloop is never destroyed, resulting in “traction” on the credit side, so Apple came up with an invalid method.
  • There are other optimizations, such as optimizing circular references using NSProxy, a virtual class dedicated to message forwarding (this is also often asked). Specific plan I do not say, their own baidu, remember, if brother you do not know this thing, I suggest you see, the probability of being asked when the interview is still very large.

24. Why need to set up the self in AFN3.0. OperationQueue. MaxConcurrentOperationCount = 1; AF2.0 does not?

  • 2. X is based on NSURLConnection, and its internal implementation needs to be in asynchronous concurrency, so 1 cannot be set. 3.0 is based on NSURLSession which internally needs to be serial and for some security reasons of multi-threaded data access, set this up to be a serial callback.

What are the differences between AFNetworking 2.0 and 3.0?

  • AFN3.0 removes all NSURLConnection request apis
  • AFN3.0 uses NSOperationQueue in place of AFN2.0’s resident thread

2. Analysis of resident thread of X version

  • After the request completes we need to do some serialization of the data, or error handling. It obviously doesn’t make sense if we deal with these things in the main line. Not only will the UI lag, but it will even be affected by the default RunLoopModel, which will cause time processing to stop when we slide the TableView.

  • This is where we need a child thread to handle events and callback requests from the network. However, the child thread automatically terminates its life cycle after processing the event, at which point we cannot receive any subsequent network request callback. So we need to enable RunLoop for the child thread to keep the thread resident.

  • Of course we could start a thread every time we make a request, but think about the overhead. So it’s a good idea to keep a thread alive to handle callback requests.

3. X version not resident thread analysis?

  • In the AFN version of 3.x, NSURLSession is used for encapsulation. In contrast to NSURLConnection, NSURLSession does not need to wait for network callbacks in the current thread, but allows the developer to set the queue for callbacks.

  • So in 3 x version use the AFN NSOperationQueue callback to network management, and setting maxConcurrentOperationCount to 1, to ensure the maximum concurrency is 1, that is to say, let the network request serial execution. The problem of resource snatch in multi-threaded environment is avoided.

25. When is autoreleasePool released?

  • All new objects in ARC are automatically added with Autorelese, and @atuorelesepool mostly solves the problem of instantaneous memory bursts.
  • The case keyword in MRC has changed to NSAutoreleasePool.
// From Apple documentation, see Reference
NSArray *urls = <# An array of file URLs #>;
for (NSURL *url in urls) { 
  @autoreleasepool { 
		NSError *error;
		NSString *fileContents = [NSString stringWithContentsOfURL:urlencoding:NSUTF8StringEncoding error:&error]; 
}

// If the number of loops is very large and the objects in the loop body are created temporarily, you can wrap them with @AutoReleasepool so that the memory of the temporary objects can be freed at the end of each loop

// For and for in are not automatically wrapped around @autoreleasepool, while the following methods are automatically wrapped around @Autoreleasepool
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // This is surrounded by a local @autoreleasepool
}];
Copy the code
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString* str = [[[NSString alloc] initWithString:@ "666"] autorelease];
[pool drain];

// it works between drain and init
Copy the code
  • Back to business @autoreleasepool when will it be released?
    • An autoRelease is automatically added to an object generated by an AutoReleasepool package after it is created. The autoRelease is automatically released at the end of the current runloop
    • Reference links: blog.sunnyxx.com/2014/10/15/…

When is the autorelease variable in the child thread released?

  • An AutoReleasepool is wrapped around the child thread by default, and is released when the current thread exits.

How is Autoreleasepool implemented?

  • Autoreleasepool {} is essentially a structure:
  • Autoreleasepool will be converted to __AtAutoreleasePool
  • __AtAutoreleasePool contains two functions objc_autoreleasePoolPush(),objc_autoreleasePoolPop(). Some of these columns actually call the push and POP methods of the AutoreleasePoolPage class
  • Push is a push operation,
  • Pop is when an out of the stack operation sends a release message to its object

###26. IOS interface rendering mechanism? [This is a very large module, which involves many things, bear with it]

  • A quick explanation of the rendering mechanism

First of all, the Core of iOS rendering view is Core Animation, and its rendering hierarchy is as follows: Layer tree -> rendering tree -> rendering tree

  • There are three stages

  • CPU stage (do the Frame layout and prepare the hierarchy between views and layers)

  • OpenGL ES stage (iOS8 later changed to Metal), (render service provided above layer color, generate various frames)

  • GPU stage (perform a series of operations on the above items and finally display them on the screen)

  • Elaborate a little

  • First, a view is framed by CPU to prepare the layer and relationship between view and layer.

  • CUP packages the hierarchy of views and layers to render services (OpenGL and GPU) via IPC (inter-process communication) channels.

  • Rendering services first hand over layers to OpenGL for texture generation and coloring, and generate frame cache before and after. Then, according to the refresh frame rate of hardware, the VSync signal of the device and CADisplayLink (similar to a timer dedicated for UI refresh) are generally used as the standard to switch between frame cache before and after

  • Finally, the post-frame cache to be displayed on the screen is delivered to GPU to collect pictures and shapes, run transformation, apply texture mixing, and finally display on the screen.

Why is the program stalling?

  • Normal rendering flow
  • After the CPU completes the calculation, it is handed to the GPU, and a synchronization signal Vsync will render the content to the screen
  • Abnormal (stutter/frame drop) flow
  • The CPU calculation time is normal or slow, and the GPU rendering time is long. At this time, the Vsync signal starts to calculate the next frame because the drawing is not complete. After the normal drawing of the next frame is successful, the current unfinished frame is discarded and the next frame is displayed.

Note that the Vsync interval is fixed. For example, Vsync with a large 60 frame rate is executed every 16ms, just like a timer

There will be an interview question!! The title is as follows:

  • What happens between when you first open your App and when you fully expose your UI? Or how does the App render a particular View?
  • The answer is a little bit of detail above, but if you want more detail, you can dig a little deeper.

Core Animation registers an Observer in the RunLoop that listens for BeforeWaiting and Exit events. This Observer has a priority of 2,000,000, which is lower than other common observers. When a touch event arrives, the RunLoop is awakened, and the code in the App performs operations such as creating and adjusting the view hierarchy, setting the UIView’s frame, modifying the CALayer’s transparency, and adding an animation to the view. These operations are eventually captured by CALayer and submitted to an intermediate state via CATransaction (the CATransaction documentation mentions these slightly, but not completely). Observers of the event are notified when the RunLoop is about to go to sleep (or exit) after all the above operations have been completed. The Observer registered with the CA then merges all intermediate states and submits them to the GPU for display in a callback; If there is an animation, the CA will trigger the relevant process multiple times through mechanisms such as DisplayLink.

2.CPU rendering function

  • Layout calculation: If the view hierarchy is too complex, it will take time to calculate the layer frame rate when trying to render or modify it.
  • View lazy loading: iOS loads the view controller’s view only when it is displayed on the screen, which is great for memory usage and application startup time, but a lot of the work caused by pressing the button is not responded to in a timely manner before rendering to the screen. For example, a controller fetching data from a data bureau, or a view loading from a XIB, or an IMAGE display involving iO will be much slower than the CPU would normally operate.
  • Unzipped images: PNG or JPEG compressed images are much smaller than bitmaps of the same quality. But before an image can be drawn on the screen, it must be expanded to its full unextracted size (usually equivalent to the image width x length x 4 bytes). To save memory, iOS usually doesn’t decode images until they’re actually drawn. Depending on how you load the image, the first time you assign the layer content (directly or indirectly using UIImageView) or draw it into Core Graphics, you’ll need to unpack it, which will take a while for a large image.
  • Core Graphics Drawing: If you implement the drawRect: or drawLayer:inContext: methods on the view, or CALayerDelegate’s methods, there is a huge performance overhead before drawing anything. To support arbitrary drawing of layer content, Core Animation must create a host image with medium memory size. Then, once the drawing is finished, the image data must be sent to the rendering server via IPC. On top of that, Core Graphics draws are very slow, so it’s not a good idea to do this in a performance-critical scenario.
  • Layer packaging: After the layers are successfully packaged and sent to the rendering server, the CPU still has to do the following: In order to display the layers on the screen, Core Animation must loop through OpenGL to convert each visible layer of the rendering tree into a texture triangle. Since the GPU doesn’t know anything about the structure of the Core Animation layer, it has to be done by the CPU. The amount of CPU work involved here is proportional to the number of layers, so if you have too many layers in your hierarchy, it will cause the CPU to render a single frame, even though these things are out of your application’s control.

3.GPU rendering Function GPU will cache data before and after the generated frames and synthesize the data according to the actual situation. In general, GPU rendering burden is caused by off-screen rendering, layer mixing and lazy loading.

Here comes another interview question!! Once a UIImageView is added to the view, how does the interior render to the phone?

The image display is divided into three steps: load, decode, and render. Normally, our programmer’s operation is just load, and the decoding and rendering are done internally by UIKit. For example, when UIImageView is displayed on the screen, the UIImage object is assigned to the data source. The data held by UIImage is undecoded compressed data. When assigned, the image data will be decoded into RGB color data and eventually rendered on the screen.


After reading the above question again! Questions about UITableView optimization? What are the reasons why UITableView stutters when scrolling?

  • Implicitly draw CGContext
  • Text CATextLayer and UILabel
  • Rasterizer shouldRasterize
  • Off-screen rendering
  • Scalable picture
  • shadowPath
  • Mix and overdraw
  • Reduce the number of layers
  • cutting
  • Object to recycle
  • The Core Graphics rendering
  • – renderInContext: method

I’m talking about UITableView optimization!

The basis of

  • Reuse mechanism (cache pool)
  • Use less transparent Views
  • Avoid xiBs whenever possible
  • Try to avoid too much hierarchy
  • The estimated height after iOS8
  • Reduce off-screen rendering operations (rounded corners, shadows, etc.)

  • **** explain why off-screen rendering operations are reduced? * * * *

  • A new buffer needs to be created

  • The whole process needs to switch the context environment several times, the display is switched from the current screen to off-screen, after the off-screen rendering is finished, the rendering result of the off-screen buffer is displayed on the screen, and the context is switched from off-screen to the current screen.

  • What actions trigger an off-screen rendering?

  • ShouldRasterize = YES

  • Mask the layer mask

  • Rounded layer.maskToBounds = Yes, layer. cornerRadis > 0

  • The shadow layer. ShadowXXX

advanced

  • Cache cell height (calculate cell height in advance and cache it in the current model)
  • Asynchronous rendering
  • When sliding, load on demand

The high order

  • You can’t believe UILabel is not recommended. Ha ha ha ~ as for why to see the link below

As for the basics above, be careful what you say when it comes to rendering levels. If the interviewer wants to mess with you, ask about the top ones, CUP and GUP, and openGL, and ask about process communication IPC, and VSync signals. These things are too advanced. No point originality It also don’t, if you want to study can see the author YYKit write an article about page smooth: blog.ibireme.com/2015/11/12/…

The Caton test method

  • An Observer can be added to the main thread Runloop to monitor the time it takes for the Runloop state to switch

Continue to

Since it’s all graphing, let’s take a look at the event response chain & principle

Here’s the traditional question: What’s the difference between UIView and CALayer? The usual answer is that UIView can respond to user events, whereas CALayer cannot handle events


Before I answer that, let’s review another classic interview question: event response chain and event delivery?

Basic Concepts:

  • Response chain: Consists of linked responders (UIResponse subclasses), usually the first responder to the application object and all intermediate responders.

  • Event passing: The process of passing the event to the first responder network application after obtaining the response chain

  • Distribution and delivery of events

  • When a touch event occurs in an application, the system adds the event to a queue managed by UIApplication

  • UIApplication sends down the event at the top of the task queue, UIWindow

  • UIWindow distributes events down, UIView or UIViewController

  • UIView first looks at whether it can handle the event, whether the touch point is on it, whether its transparency is greater than 0,01, whether userInteractionEnabled is YES, Hidden is actually NO, and if that’s all right, then it goes on looking for its subview

  • Iterate over the child control, repeating the previous steps

  • If you don’t find it, then you are the handler of the event

  • If it can’t handle it, then it does nothing and no View is deemed suitable to receive and handle the current event, and the modified event is discarded.

  • How to find which View is currently touched? * * *

The following two methods

// The View returned by this method is the best View needed for this click event
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

// Determine whether a point falls within the range
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
Copy the code

After the event is passed to the control, it will call the hitTest:withEvent method to find a more suitable View. If the current View has a child control, it will continue to call the hitTest:withEvent method in the child control to determine whether the View is suitable. If you can’t find it, throw it away.

// Because all view classes inherit BaseView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
   // 1. Check whether the current control can receive events
   if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
   // 2. Check whether the point is in the current control
   if ([self pointInside:point withEvent:event] == NO) return nil;
   // 3. Traverse its child controls from back to front
   NSInteger count = self.subviews.count;
   for (NSInteger i = count - 1; i >= 0; i--) {
      UIView *childView = self.subviews[I];
       // Convert the coordinate system on the current control to the coordinate system on the child control
	  CGPoint childP = [self convertPoint:point toView:childView];
      UIView *fitView = [childView hitTest:childP withEvent:event];
       if (fitView) { // Find the most appropriate view
           returnfitView; }}// The loop ends, indicating that no view is more suitable than yours
   return self;
   
}
Copy the code
  • Is the touch point in the view?
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
Copy the code
  • TableView with a tap gesture, click on the current cell position which event will be responded to why?
  • Tap events are responded to because tap events are added to cancel all events except the current tap by default, that is, the tap event is at the top of the current responder chain. The solution is to execute the DELagete of tap
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch  
{  
    if([touch.view isKindOfClass:[XXXXcell class]])  
    {  
        return NO;  
    }  
    return YES;  
}

Copy the code

The next one is here juejin.cn/post/685457…