In July, there were relatively few job-hopping on iOS. Those who could apply for jobs during this period were either forced or confident in their skills. For July, I specially summarized the third iOS common daco interview questions (ii);

IOS interview questions are divided into three parts: top, middle and bottom, which are convenient for you to watch.

Please answer by yourself first!

Don’t say much; This article is directly on the topic: the public number [iOS advanced treasure book “iOS bottom level test questions (next)”]

13. How to catch HTTPS packets with Charles? What is the principle and process?

Process:

  • First install the Charles certificate on your phone

  • Enable SSL Proxying in the proxy Settings

  • Then add the address of the server that needs to be fetched

Principle:

Charles acts as a middleman, posing as a server to the client and a client to the server. In short:

  • Intercepts HTTPS requests from clients and sends HTTPS requests to servers disguised as middleman clients
  • The receiving server returns and sends data contents to the client using its own certificate disguised as a middleman server.

The process is as follows: Pull pull HTTPS unidirectional authentication, two-way authentication, packet capture principles, and anti-packet capture policies

What is a man-in-the-middle attack? How to avoid it?

A man-in-the-middle attack is the interception of the client’s request and the server’s response. For example, Charles grabbing HTTPS packets is a man-in-the-middle attack.

What to avoid: The client can embed certificates locally and compare the certificates for matches

15. Do you understand the compilation process? What are the steps?

1: precompile: Mainly deals with precompile instructions starting with “#”.

2: compile:

  • Lexical analysis: Splitting a sequence of characters into a series of tokens.

  • Syntax analysis: Generates a syntax tree based on the generated tokens.

  • Semantic analysis: Analyze the semantics of syntax tree, and carry out type matching, conversion, identification and so on.

  • Intermediate code generation: The source-level optimizer converts the syntax tree into intermediate code and performs source level optimizations, such as optimizing 1+2 to 3. The intermediate code divides the compiler into front end and back end, and different platforms can use different compiler backends to convert the intermediate code into machine code, making it cross-platform.

  • Object code generation: The rest of the process is the back end of the compiler. The code generator converts the intermediate code into object code (assembly code), after which the object code optimizer optimizes the object code, such as adjusting the addressing mode, using displacement instead of multiplication, removing redundant instructions, adjusting the order of instructions, etc.

3: Assembly: The assembler converts assembly code into machine instructions.

  • Static linking: The linker links together object files that have been compiled into machine instructions and outputs an executable file after relocating.

  • Load: Load the executable file and the shared objects it depends on.

  • Dynamic linking: The dynamic linker fixes the locations in executable files and shared objects that need to be relocated.

Finally, control of the process is transferred to the program portal, and the program is finally running.

16. What about static links? What’s the difference between a static library and a dynamic library?

Static linking is the process of merging multiple object files into a single executable file. The intuitive idea is to merge segments of all object files. It is important to note that the structure of executable file and object file is basically the same, the difference is whether “executable”.

  • Static library: a complete copy to an executable file when linked, and multiple copies if used multiple times.

  • Dynamic library: the link is not copied, the program is dynamically loaded into memory by the system when running, for the program to call, the system only loads once, multiple programs shared, save memory.

17. What are the optimization strategies for the App network layer?

  • Optimize DNS resolution and caching

  • Compress the transmitted data to reduce the transmitted data

  • Use caching to reduce the number of requests initiated

  • Use policies to reduce the number of requests made, such as not making new requests until the last one has landed

  • Avoids network jitter and provides the retransmission mechanism

18: [self class] and [super class]

@implementation Son : Father

(id)init
    {
    self = [super init];
    if (self)
    {
    NSLog(@"%@", NSStringFromClass([self class]));
    NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
    }
    @end
Copy the code

The difference between self and super:

  • selfIs a hidden parameter of the class. The first parameter of each method implementation isself.
  • Super isn’t a hidden argument, it’s really just a “compiler identifier” that tells the compiler to call methods from the parent class, not from this class, when calling methods.

When calling [super class], Runtime will call objc_msgSendSuper, not objc_msgSend

OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ ) /// Specifies the superclass of an instance. struct objc_super { /// Specifies an instance of a class. __unsafe_unretained id receiver; /// Specifies the particular superclass of the instance to message. # if ! defined(__cplusplus) && ! __OBJC2__ /* For compatibility with old objc-runtime.h header */ __unsafe_unretained Class class; # else __unsafe_unretained Class super_class; # endif /* super_class is the first class to search */ }Copy the code

In the objc_msgSendSuper method, the first argument is an objc_super structure that contains two variables: receiver, which receives messages, and super_class, which is the parent class of the current class.

Start looking for selectors from the superClass method list that the objc_super structure points to, and when the superClass finds it, the superClass executes the method.

Internal implementation of class methods:

- (Class)class {
return object_getClass(self);
}
Copy the code

Within a class method, self is passed in by default, regardless of who called it.

So here’s the answer to this question: Both prints are the current class.

18. IsKindOfClass and isMemberOfClass

What does the following code output?

@interface Sark : NSObject
@end

@implementation Sark
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];

NSLog(@"%d %d %d %d", res1, res2, res3, res4);
}
return 0;
}
Copy the code

First, let’s analyze the source code for the object implementation of these two functions

+ (Class)class { return self; } (Class)class { return object_getClass(self); } Class object_getClass(id obj) { if (obj) return obj->getIsa(); else return Nil; } inline Class objc_object::getIsa() { if (isTaggedPointer()) { uintptr_t slot = ((uintptr_t)this >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK; return objc_tag_classes[slot]; } return ISA(); } inline Class objc_object::ISA() { assert(! isTaggedPointer()); return (Class)(isa.bits & ISA_MASK); } (BOOL)isKindOfClass:(Class)cls { for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; } (BOOL)isKindOfClass:(Class)cls { for (Class tcls = [self class]; tcls; tcls = tcls->superclass) { if (tcls == cls) return YES; } return NO; } (BOOL)isMemberOfClass:(Class)cls { return object_getClass((id)self) == cls; } (BOOL)isMemberOfClass:(Class)cls { return [self class] == cls; }Copy the code

So the first thing they do is they call class methods in NSObject and Sark.

  • + (BOOL)isKindOfClass:(Class) CLS (object_getClass); (obj->getIsa(); Finally, we get a pointer to the Meta class in the ISA() method.

  • And then we have a loop in isKindOfClass, and we judge if class is equal to meta class, and we continue to loop if class is equal to super class, and we continue to loop if class is equal to Super class, and we continue to loop if class is equal to Super class, and so on.

  • NSObject class, and then we call isKindOfClass, and the first judgment is whether the meta class of NSObject and NSObject are equal, and we put a very detailed picture of the Meta class, and we can see from the picture, NSObject’s Meta class is different from itself.

  • Then the second loop determines whether NSObject is equal to the superclass of the Meta class. The superclass of Root Class (Meta) is Root class(Class), which is NSObject itself. So the second loop is equal, so the output of the first line res1 should be YES.

  • For the first loop, the Meta class of Sark is different from the Meta class of [Sark]. For the second loop, The super Class of the Sark Meta Class refers to the NSObject Meta Class, which is not equal to the Sark Class.

  • The third for loop, the super Class of the NSObject Meta Class refers to the NSObject Class, which is not equal to the Sark Class. On the fourth loop, the super Class of NSObject Class points to nil, which is not equal to the Sark Class. After the fourth loop, exit the loop, so the third line res3 prints NO.

  • If we change Sark to its instance object, [Sark isKindOfClass:[Sark class], then we should print YES, because in isKindOfClass, the meta class of Sark is its own metaclass, Sark. The first time the for loop will print YES.

  • IsMemberOfClass source code implementation is to get its own ISA pointer and compare to itself, whether equal.

  • The second line isa points to the Meta Class of NSObject, so it’s not equal to the NSObject Class. In the fourth line, isa points to the Meta Class of Sark, which is also different from the Sark Class, so the second line res2 and the fourth line res4 both print NO.

19.Class and memory address

The following code will? **Compile Error/Runtime Crash/NSLog… ? 六四事件

@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;

(void)speak;
    @end
    @implementation Sark
(void)speak {
    NSLog(@"my name's %@", [self.name](http://self.name/));
    }
    @end
    @implementation ViewController
(void)viewDidLoad {
    [super viewDidLoad];
    id cls = [Sark class];
    void *obj = &cls;
    [(__bridge id)obj speak];
    }
    @end
Copy the code

There are two difficult points in this problem.

  • The difficulties in a:objcallspeakMethod, whether or not it will crash.
  • Difficulty 2: IfspeakMethod does not crash, what should be output?

First, I need to talk about the hidden arguments self and _cmd. When a method is called by [Receiver Message], two hidden arguments, self and _cmd, are secretly passed in dynamically at runtime. They are called hidden arguments because they are not declared or defined in the source code. Self gets it, so let’s talk about _cmd. _cmd indicates the currently invoked method, which is essentially a method selector SEL.

Difficulty one, can you callspeakMethods?

id cls = [Sark class];
void *obj = &cls;
Copy the code

The answer is yes. Obj is converted to a pointer to Sark Class and then converted to objc_object using id. Obj is now an instance of type Sark. Of course, you can then call the speak method.

Difficulty number two, if you can callspeak, what will be output?

A lot of people might think that it’s going to output information about the SARK. That would be the wrong answer.

The correct answer will be printed

my name is <ViewController: 0x7ff6d9f31c50>

The memory address is different every time we run it, but it must be the ViewController before it. According to?

Let’s change the code and print out more information.

- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"ViewController = %@, address = %p", self, &self); id cls = [Sark class]; NSLog(@"Sark class = %@ address = %p", CLS, &cls); void *obj = &cls; NSLog(@"Void *obj = %@ address = %p", obj,&obj); [(__bridge id)obj speak]; Sark *sark = [[Sark alloc]init]; NSLog(@"Sark instance = % @address = %p", Sark,&sark); [sark speak]; }Copy the code

We print out the address of the pointer to the object. Output results:

ViewController = <ViewController: Sark class = Sark address = 0x7ffF543f5a88 Void *obj = <Sark: My name is <ViewController: 0x7FB570e2ad00 > Sark instance = <Sark: 0x7fb570d20b10> address = 0x7ffF543F5a78Copy the code

Objc_msgSendSuper2 interpretation

// objc_msgSendSuper2() takes the current search class, not its superclass.
OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_2_0);
Copy the code

The objc_msgSendSuper2 method takes an objc_super *super.

/// Specifies the superclass of an instance. struct objc_super { /// Specifies an instance of a class. __unsafe_unretained id receiver; /// Specifies the particular superclass of the instance to message. # if ! defined(__cplusplus) && ! **OBJC2** /* For compatibility with old objc-runtime.h header */ __unsafe_unretained Class class; # else __unsafe_unretained Class super_class; # endi /* super_class is the first class to search */ }; # endifCopy the code

So in viewDidLoad the order of the variables pushed from top to bottom is self, _cmd, self.class, self, obj.

  • The first self and the second _cmd are the hidden arguments.

  • The third self.class and fourth self are arguments to the [super viewDidLoad] method.

  • When calling self.name, it essentially offsets the self pointer to a higher address in memory. Under 32 bits, a pointer is 4 bytes =4*8 bits =32 bits. (64-bit is different but the idea is the same)

  • From the print, we can see that obj is the address of CLS. Offset obj by 32bit to 0x7FFF543F5aa8, which is the address of the ViewController.

My name is **

So, what exactly are objects in Objc?

Essence: The object in Objc is a variable pointing to the address of the ClassObject, that is, id obj = &classobject, and the instance variable of the object is void *ivar = &obj + offset(N).

To further understand the above statement, what does this code output?

- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"ViewController = %@, address = %p", self, &self); NSString *myName = @"halfrost"; id cls = [Sark class]; NSLog(@"Sark class = %@ address = %p", CLS, &cls); void *obj = &cls; NSLog(@"Void *obj = %@ address = %p", obj,&obj); [(__bridge id)obj speak]; Sark *sark = [[Sark alloc]init]; NSLog(@"Sark instance = % @address = %p", Sark,&sark); [sark speak]; } ViewController = <ViewController: Sark class = Sark class = 0x7ffF56a48a50 Void *obj = <Sark: 0x7ffF56a48a50 > address = 0x7ffF56A48A48 0x6080000233E0 > Address = 0x7FFF56A48A40 My name is (null)Copy the code

By adding a string, the output changes completely, [(__bridge ID)obj speak]; This will print “My name is halfrost.”

Again, the reasons are similar. In the order of viewDidLoad, each variable is pushed from the top down to self, _cmd, self.class, self, myName, obj. Obj is offset up 32 bits, which is the string myName, so the output becomes output myName.

20. Sort questions: Bubble sort, choice sort, insert sort, quick sort (2, 3) can write what?

Here under the simple said the difference between several quick sort, random fast row, is orderly, in order to solve the approximate time complexity will degenerate into O (n ^ 2), dual fast row is in order to solve the quick sort in the case of a large amount of data repeat, time complexity will degenerate into O (n ^ 2), three way fast row is in a large amount of data under the condition of repetition, An optimization of dual – route express platoon.

  • Bubble sort
extension Array where Element : Comparable{ public mutating func bubbleSort() { let count = self.count for i in 0.. <count { for j in 0.. <(count - 1 - i) { if self[j] > self[j + 1] { (self[j], self[j + 1]) = (self[j + 1], self[j]) } } } } }Copy the code
  • Selection sort
extension Array where Element : Comparable{ public mutating func selectionSort() { let count = self.count for i in 0.. <count { var minIndex = i for j in (i+1).. <count { if self[j] < self[minIndex] { minIndex = j } } (self[i], self[minIndex]) = (self[minIndex], self[i]) } } }Copy the code
  • Insertion sort
extension Array where Element : Comparable{ public mutating func insertionSort() { let count = self.count guard count > 1 else { return } for i in 1.. <count { var preIndex = i - 1 let currentValue = self[i] while preIndex >= 0 && currentValue < self[preIndex] { self[preIndex + 1] = self[preIndex] preIndex -= 1 } self[preIndex + 1] = currentValue } } }Copy the code
  • Quick sort
extension Array where Element : Comparable{
public mutating func quickSort() {
func quickSort(left:Int, right:Int) {
guard left < right else { return }
var i = left + 1,j = left
let key = self[left]
while i <= right {
if self[i] < key {
j += 1
(self[i], self[j]) = (self[j], self[i])
}
i += 1
}
(self[left], self[j]) = (self[j], self[left])
quickSort(left: j + 1, right: right)
quickSort(left: left, right: j - 1)
}
quickSort(left: 0, right: self.count - 1)
}
}
Copy the code
  • Random fast row
extension Array where Element : Comparable{ public mutating func quickSort1() { func quickSort(left:Int, right:Int) { guard left < right else { return } let randomIndex = Int.random(in: left... right) (self[left], self[randomIndex]) = (self[randomIndex], self[left]) var i = left + 1,j = left let key = self[left] while i <= right { if self[i] < key { j += 1 (self[i], self[j]) = (self[j], self[i]) } i += 1 } (self[left], self[j]) = (self[j], self[left]) quickSort(left: j + 1, right: right) quickSort(left: left, right: j - 1) } quickSort(left: 0, right: self.count - 1) } }Copy the code
  • Dual fast row
extension Array where Element : Comparable{ public mutating func quickSort2() { func quickSort(left:Int, right:Int) { guard left < right else { return } let randomIndex = Int.random(in: left... right) (self[left], self[randomIndex]) = (self[randomIndex], self[left]) var l = left + 1, r = right let key = self[left] while true { while l <= r && self[l] < key { l += 1 } while l < r && key < self[r]{ r -= 1 } if l > r { break } (self[l], self[r]) = (self[r], self[l]) l += 1 r -= 1 } (self[r], self[left]) = (self[left], self[r]) quickSort(left: r + 1, right: right) quickSort(left: left, right: r - 1) } quickSort(left: 0, right: self.count - 1) } }Copy the code
  • Three way fast row
// Extension Array where Element: Comparable{ public mutating func quickSort3() { func quickSort(left:Int, right:Int) { guard left < right else { return } let randomIndex = Int.random(in: left... right) (self[left], self[randomIndex]) = (self[randomIndex], self[left]) var lt = left, gt = right var i = left + 1 let key = self[left] while i <= gt { if self[i] == key { i += 1 }else if self[i] < key{ (self[i], self[lt + 1]) = (self[lt + 1], self[i]) lt += 1 i += 1 }else { (self[i], self[gt]) = (self[gt], self[i]) gt -= 1 } } (self[left], self[lt]) = (self[lt], self[left]) quickSort(left: gt + 1, right: right) quickSort(left: left, right: lt - 1) } quickSort(left: 0, right: self.count - 1) } }Copy the code