Coobjc, an Apache 2.0-based coouten development framework, has just been opened to the public. Developers can download it on Github. Coobjc is an open source coroutine development framework for iOS platform, supporting Objective-C and Swift. Meanwhile, it provides cokit library to provide coroutine support for some apis in Foundation and UIKit. This paper will introduce the design concept and core advantages of COOBJC in detail.


Open source address

Github.com/alibaba/coo…

IOS asynchronous programming issues

In the 11 years since the first iOS release in 2008, asynchronous programming has been slow to take off.

At present, asynchronous programming callback based on Block is the most widely used asynchronous programming method in iOS. The GCD library provided by iOS system makes asynchronous development very simple and convenient. However, there are also many disadvantages based on this programming method, mainly including the following:

  • Easy access to “nested hell”
  • Error handling is complex and verbose
  • It’s easy to forget to call the Completion handler
  • Conditional execution becomes difficult
  • It becomes extremely difficult to combine returns from calls that are independent of each other
  • Continue execution in the wrong thread (e.g. child thread manipulating UI)
  • Multi-thread crash that is difficult to locate the cause (multi-thread crash has accounted for more than 60% of hand shopping)
  • Lock and semaphore abuse can cause stuttering and stalling

Aimed at the collapse and multithreading and especially can cause performance problems, we have a lot of programming specifications, the various new training, try to reduce the probability of a problem, but the problem is still very grim, multithreading causes of problems and no obvious drop, asynchronous programming is very complicated things, specification and training alone is difficult to fundamentally solve the problem, There needs to be a better way of programming.

The solution

The above problems may be encountered in many systems and language development, the standard way to solve the problem is to use coroutines, C#, Kotlin, Python, Javascript and other popular languages support coroutines and related syntax, developers using these languages can be very convenient to use coroutines and related functions for asynchronous programming.

In 2017, THE C++ standard began to support coroutines, and Swift5 also includes coroutines related standards. From the current development trend, the new asynchronous programming method based on coroutines is an effective way to solve the existing asynchronous programming problems. However, Apple has basically stopped upgrading Objective-C. Therefore, developers using Objective-C cannot use the official coroutine capability, and it will take some time for the release and promotion of the latest Swift. In order to allow the majority of iOS developers to quickly enjoy the changes in programming methods brought by coroutine, the mobile Taobao architecture team based on long-term research on the underlying system library and assembly. Coobjc is a perfect solution supporting Objective-C and Swift coroutines through assembly and C language.

Core competence

  • It provides support for Async/Await programming mode similar to C# and Javascript languages. The execution results of asynchronous methods can be synchronously obtained by calling Await methods in coroutines, which is very suitable for synchronous sequential execution transformation of IO, network and other asynchronous time-consuming calls.
  • Provides Generator functionality similar to that found in Kotlin for lazy computing to generate serialized data, ideal for multithreaded interruptable serialized data generation and access.
  • Provides the realization of the Actor Model, based on the Actor Model, developers can develop a more thread-safe module, to avoid a variety of multithreaded crash problems caused by direct function calls.
  • Support for tuples is provided, through which objective-C developers can enjoy the benefits of multi-value returns similar to those found in Python.

Built-in system extension library

  • Coprogramming extensions to container libraries such as NSArray and NSDictionary are provided to solve asynchronous call problems during serialization and deserialization.
  • It provides the coroutine extension for data objects such as NSData, NSString, UIImage, etc., to solve the asynchronous call problem in the process of reading and writing I/O.
  • Provides coroutine extensions to NSURLConnection and NSURLSession to solve the problem of asynchronous invocation in the process of asynchronous network requests.
  • It provides extensions to NSKeyedArchieve, NSJSONSerialization and other parsing libraries to solve the problem of asynchronous calls during parsing.

Coobjc design

The bottom layer is the coroutine kernel, which contains the management of stack switch, the implementation of coroutine scheduler, the implementation of communication channel between coroutines and so on.

The middle layer is a wrapper around coroutine based operators and currently supports async/await, Generator, Actor and other programming models.

The top layer is a coroutine extension to the system library that currently covers almost all IO and time consuming methods of Foundation and UIKit.

Core Implementation Principles

The core idea of coroutines is to control the active relinquishing and recovery of the call stack. A typical coroutine implementation provides two important operations:

  • Yield: To Yield the CPU, it interrupts the current execution and returns to the place where it last resumed.
  • Resume: Resumes the coroutine. After Resume is executed, the coroutine returns to Yield.

We can’t pause our thread-based code, so what we need to do now is to make it pause and then resume. Basically, code execution is based on the call stack, so if we can save all the current state on the call stack and then recover it from the cache, we can achieve yield and resume.

How many ways are there to do this?

  • First: Use gliBC’s UContext component (a library for cloud wind).
  • Second: use assembly code to switch contexts (implementing C coroutines) in the same way as uContext.
  • Third: using C language syntax switch-case strange skills to achieve (Protothreads).
  • Fourth: the use of C language setjMP and longjMP.
  • Fifth: use the compiler to support syntax sugar.

The third and fourth methods above can only jump, but can not save the state on the call stack, it seems that basically can not be regarded as the implementation of coroutine, can only be counted as a demo, the fifth method unless officially supported, or rewrite the compiler is not universal. The uContext of the first solution is deprecated on iOS and cannot be used. So we’re going to use the second option, which is to simulate the UContext ourselves in assembly.

The core of emulating uContext is to save and restore the call stack through getContext and setContext. You need to be familiar with the Calling Convention for different CPU architectures. Assembler implementation is to implement a set for different CPUS, we currently implement ARMV7, ARM64, I386, X86_64, support iPhone real machine and emulator.

Show me the code

With that said, let’s look at the code and see how CoobJC works by loading images from a simple network request.

Here is the most common way to write a network request:

Here is the code after the coprogramming transformation using cooBJC library:

Coobjc coprogramming reduces the original 20 lines of code by half, making the whole code more logical and readable. This is cooBJC’s powerful ability to transform complex asynchronous code into logical and concise sequential calls through coprogramming.

Coobjc has many other powerful capabilities, but this article will not cover the actual use of CooBJC. If you are interested, you can download it from the official Github repository.

Performance improvement

We used coroutine and traditional multithreading methods to simulate high concurrent data reading scenarios respectively on iPhone7 iOS11.4.1 devices. The pressure measurement data obtained by the two methods are as follows.

  • Test machine: iPhone7 iOS11.4.1
  • Data file size: 20M
  • Maximum number of threads used by coroutines: 4
  • Data test results (counting the total time it took for all concurrent access to end) :




We tried in the mobile phone taobao this super App association programs, for part of the page, about the worse performance of we found that there are a lot of the main thread in the process of sliding IO call, data parsing, led to the decrease of the frame rate, by introducing coobjc, without changing the original business code, on the basis of using global hook part IO, data analysis method, In this way, the IO method that was executed synchronously in the main thread can be executed asynchronously without affecting the original business logic. Through test verification, the frame rate of such transformation on low-end machines (iPhone6 and below) has been improved by about 20%.

advantage

concise

  • Few concepts: There are only a few operators, compared to dozens of reactive operators, which is incredibly simple.
  • Simple principle: The implementation principle of coroutines is very simple, the entire coroutine library is only a few thousand lines of code.

Easy to use

  • Easy to use: It is simpler to use than GCD, with fewer interfaces.
  • Easy to change: existing code can be coroutined with very few changes, and we provide a large number of coroutined interfaces for the system library.

clear

  • Synchronous writing asynchronous logic: Synchronous sequential writing is the most acceptable way for humans to write code, which greatly reduces the probability of errors.
  • Readable: Code written using coroutines is much more readable than code nested with blocks.

performance

  • Faster scheduling performance: Coroutines themselves do not require kernel-level thread switching, scheduling performance is fast, even if the creation of thousands of coroutines is no pressure.
  • Reduce latency: The use of coroutines helps to reduce lock and semaphore abuse. By encapsulating coroutine interfaces such as IO that cause blocking, the use of coroutines can reduce latency and latency at the root and improve the overall performance of the application.

conclusion

Programs are written to be read and only occasionally executed by machines. – Abelson and Sussman

The programming paradigm based on coroutine implementation can help developers write more elegant, robust, and readable code.

Coroutines can help reduce the use of threads and locks while writing concurrent code, improving application performance and stability.

This article is from the cloud community partner “Ali Technology”, to understand the relevant information can pay attention to “Ali technology”.