I have a colleague who is neither a Kim nor a driver, but we all call him “Driver Kim”. He’s an iOS engineer like a hamster, and it’s not hard to figure out why he called the driver… In case the blog is blocked, no examples are given here.

In short, Driver Jin showed several of his “interview questions” to colleagues in the group at the weekly meeting this week, successfully eliminated all colleagues in the group, even our boss, and brought a lot of joy to the dull work. I use quotation marks because these questions are designed to look like interview questions and are not really used for an interview (and our company would never use these questions for an interview), otherwise we might not be able to hire anyone. If you’re interested, don’t spray my colleague

So this code is going to be executed in the Command line environment, and even though it’s written by Swift, it’s going to have the same API, so anyone who’s writing Objective-C can read it. Let’s get started

Main thread and main queue

Before looking at this set of questions, ask yourself a question: What is the relationship between the main thread and the main queue?

The first question

let key = DispatchSpecificKey<String>()

DispatchQueue.main.setSpecific(key: key, value: "main")

func log() {
  debugPrint("main thread: \(Thread.isMainThread)")
  let value = DispatchQueue.getSpecific(key: key)
  debugPrint("main queue: \(value ! = nil)")
}

DispatchQueue.global().sync(execute: log)
RunLoop.current.run()
Copy the code

What is the result of the execution?

The second question

let key = DispatchSpecificKey<String>()

DispatchQueue.main.setSpecific(key: key, value: "main")

func log() {
  debugPrint("main thread: \(Thread.isMainThread)")
  let value = DispatchQueue.getSpecific(key: key)
  debugPrint("main queue: \(value ! = nil)")
}

DispatchQueue.global().async {
  DispatchQueue.main.async(execute: log)
}
dispatchMain()
Copy the code

When is the output not double true?

The GCD and OperationQueue

The third question

let observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.allActivities.rawValue, true, 0) { _, activity in
  if activity.contains(.entry) {
    debugPrint("entry")}else if activity.contains(.beforeTimers) {
    debugPrint("beforeTimers")}else if activity.contains(.beforeSources) {
    debugPrint("beforeSources")}else if activity.contains(.beforeWaiting) {
    debugPrint("beforeWaiting")}else if activity.contains(.afterWaiting) {
    debugPrint("afterWaiting")}else if activity.contains(.exit) {
    debugPrint("exit")
  }
}

CFRunLoopAddObserver(CFRunLoopGetMain(), observer, CFRunLoopMode.commonModes)

// case1 DispatchQueue.global().async { (0... 999).forEach { idxin
    DispatchQueue.main.async {
      debugPrint(idx)
    }
  }
}

// case 2
//DispatchQueue.global().async {
//  letoperations = (0... 999).map { idxin BlockOperation { debugPrint(idx) } }
//  OperationQueue.main.addOperations(operations, waitUntilFinished: false)
//}

RunLoop.current.run()
Copy the code

How will the above GCD be different from the commented out OperationQueue print?

Thread safety

The fourth question

Objective-c is a little different from Swift, so I’ve provided two versions of the code:

Swift:

let queue1 = DispatchQueue(label: "queue1")
let queue2 = DispatchQueue(label: "queue2")

var list: [Int] = []

queue1.async {
  while true {
    if list.count < 10 {
      list.append(list.count)
    } else {
      list.removeAll()
    }
  }
}

queue2.async {
  while true {
    // case 1
    list.forEach { debugPrint($0} / /case2 / /let value = list
//    value.forEach { debugPrint($0} / /case 3
//    var value = list
//    value.append(100)
  }
}

RunLoop.current.run()
Copy the code

Will the case 1 code crash? Case 2? Case 3?

Objective-C:

    dispatch_queue_t queue1 = dispatch_queue_create("queue1", 0);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", 0);
    
    NSMutableArray* array = [NSMutableArray array];

    dispatch_async(queue1, ^{
      while (true) {
        if (array.count < 10) {
          [array addObject:@(array.count)];
        } else{ [array removeAllObjects]; }}}); dispatch_async(queue2, ^{while (true{/ /case1 / /for (NSNumber* number in array) {
//          NSLog(@"% @", number); / / / /}case 2
//        NSArray* immutableArray = array;
//        for (NSNumber* number in immutableArray) {
//          NSLog(@"% @", number); / / / /}case 3
        NSArray* immutableArray = [array copy];
        for (NSNumber* number in immutableArray) {
          NSLog(@"% @", number); }}}); [[NSRunLoop currentRunLoop] run];Copy the code

Will the case 1 code crash? Case 2? Case 3?

Runloop

The fifth problem

class Object: NSObject {
  @objc
  func fun() {
    debugPrint("\(self) fun")
  }
}

var runloop: CFRunLoop!

let sem = DispatchSemaphore(value: 0)

let thread = Thread {
  RunLoop.current.add(NSMachPort(), forMode: .commonModes)

  runloop = CFRunLoopGetCurrent()

  sem.signal()

  CFRunLoopRun()
}

thread.start()

sem.wait()

DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  CFRunLoopPerformBlock(runloop, CFRunLoopMode.commonModes.rawValue) {
    debugPrint("2")
  }

  DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
    debugPrint("1")
    let object = Object()
    object.fun()
//    CFRunLoopWakeUp(runloop)
  })
}

RunLoop.current.run()
Copy the code

So what does that output?

The answer

The first question:

"main thread: true"
"main queue: false"
Copy the code

See that other queues can also run on the main thread.

The second problem: this one is not easy to come up with. So take a screenshot:

Look, the main queue is not on the main thread!

The API dispatchMain() used here, if changed to runloop.current.run (), results in double true as we would normally expect. This is true only if the command line environment is used. If the project is an iOS app with runloop, the result will also be double true.

Question 3: GCD:

"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
"afterWaiting"
"exit"
"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
"afterWaiting"0 1 2 3 4... 996 997 998 999"exit"
"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
"afterWaiting"
"exit"
"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
Copy the code

OperationQueue

"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
"afterWaiting"
0
"exit"
"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
"afterWaiting"
1
"exit"
"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
"afterWaiting"
2
"exit"
"entry"
"beforeTimers"
"beforeSources"
"beforeWaiting"
"afterWaiting".Copy the code

This example shows that an OperationQueue is slightly less likely to stall than a GCD when a large number of tasks are dispatched.

4 题 : This question is actually quite practical, the answer is that each case of two languages will >< [NSArray copy] that probability is lower, but a little run for a while or very easy to trigger.

The third case of Objective-C is really different from the previous two crash cases. The error message is a release of something that has already been released. I don’t know why this happens, but the problem is probably in the copy method’s internal implementation.

Problem 5: the above code directly run is

"1"
"<Runloop.Object: 0x102d05be0> fun"
Copy the code

Perform (#selector(object.fun), on: thread, with: nil, waitUntilDone: perform(#selector(object.fun), on: thread, with: nil, waitUntilDone: (false) print 2. If the runloop is in sleep state, performSelector will wake up the runloop, but a single call will not.

One detail is that if you do CFRunLoopWakeUp(runloop), the output order is 1 fun 2 whereas if you do performSelector it’s 1 fun 2 fun. My friends ride god’s explanation:

The timer task added during the Perform call wakes up the Runloop to process the task. But because the CFRunLoopPerformBlock task is queued earlier, the output takes precedence over fun

Answer key

Hamster was going to shamelessly write a paid article and pay for the solution, which would have made a fortune 🙂 but since hamster is a bit of a foodie, I’m not going to provide the solution. You are welcome to discuss it in the comments section, and I will post links to your answers

Riding god’s solution to the first and second questions

Afterword.

Hamster companies are also hiring. Because before writing blog was sprayed, still have lingering fear; So for fear of being criticized, I dare not say which company (is there such a recruitment? In short, it is a foreign Internet company, Beijing coordinates. Most swift, obviously my colleagues and boss are very skilled, and hamsters are the worst here. And everyone is nice, and the company’s benefits are among the best in the industry. Our interview questions are very practical, mainly to write code on the spot to achieve small functions, 100% of the most commonly used in daily work, will not use the above strange questions, you can rest assured. If required, we only recruit senior candidates at this stage. Basically, we need to have more than 4 years of experience. It is better to have experience in a big factory or have a good school background. I can answer all kinds of questions about the job and interview. If this article is the reason for the recommendation, I will also pass it on to my colleague Driver Kim