├── 协程 Demo.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── zhanghong.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcuserdata │ └── zhanghong.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── xcschememanagement.plist │ │ ├── ZHCoroutine.xcscheme │ │ └── 协程 Demo.xcscheme └── project.pbxproj ├── 协程 Demo ├── ViewController.h ├── AppDelegate.h ├── main.m ├── Info.plist ├── Base.lproj │ ├── Main.storyboard │ └── LaunchScreen.storyboard ├── ViewController.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json └── AppDelegate.m ├── ZHCoroutine ├── Coroutine.h ├── Coroutine.s └── Coroutine.c ├── 协程 DemoTests ├── Info.plist └── ___DemoTests.m ├── 协程 DemoUITests ├── Info.plist └── ___DemoUITests.m └── README.md /协程 Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /协程 Demo.xcodeproj/project.xcworkspace/xcuserdata/zhanghong.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suancaiAmour/CoroutineDemo/HEAD/协程 Demo.xcodeproj/project.xcworkspace/xcuserdata/zhanghong.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /协程 Demo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/21. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /协程 Demo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/21. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /协程 Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/21. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ZHCoroutine/Coroutine.h: -------------------------------------------------------------------------------- 1 | // 2 | // Coroutine.h 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/28. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | #ifndef Coroutine_h 10 | #define Coroutine_h 11 | 12 | #include 13 | 14 | typedef void (*coroutineTask)(void); 15 | 16 | void coroutine_switch(void); 17 | void coroutine_release(void); 18 | void coroutine_start(coroutineTask entryTask); 19 | void coroutine_create(coroutineTask task); 20 | 21 | #endif /* Coroutine_h */ 22 | -------------------------------------------------------------------------------- /协程 Demo.xcodeproj/xcuserdata/zhanghong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /协程 DemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /协程 DemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /协程 DemoTests/___DemoTests.m: -------------------------------------------------------------------------------- 1 | //___FILEHEADER___ 2 | 3 | #import 4 | 5 | @interface ___FILEBASENAMEASIDENTIFIER___ : XCTestCase 6 | 7 | @end 8 | 9 | @implementation ___FILEBASENAMEASIDENTIFIER___ 10 | 11 | - (void)setUp { 12 | [super setUp]; 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | } 15 | 16 | - (void)tearDown { 17 | // Put teardown code here. This method is called after the invocation of each test method in the class. 18 | [super tearDown]; 19 | } 20 | 21 | - (void)testExample { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | - (void)testPerformanceExample { 27 | // This is an example of a performance test case. 28 | [self measureBlock:^{ 29 | // Put the code you want to measure the time of here. 30 | }]; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /协程 Demo.xcodeproj/xcuserdata/zhanghong.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZHCoroutine.xcscheme 8 | 9 | isShown 10 | 11 | orderHint 12 | 2 13 | 14 | 协程 Demo.xcscheme 15 | 16 | isShown 17 | 18 | orderHint 19 | 0 20 | 21 | 22 | SuppressBuildableAutocreation 23 | 24 | FC2789A71FC348C5006039B6 25 | 26 | primary 27 | 28 | 29 | FC2789BF1FC348C5006039B6 30 | 31 | primary 32 | 33 | 34 | FC2789CA1FC348C5006039B6 35 | 36 | primary 37 | 38 | 39 | FCCEA8ED1FDF505A00182EA7 40 | 41 | primary 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /协程 DemoUITests/___DemoUITests.m: -------------------------------------------------------------------------------- 1 | //___FILEHEADER___ 2 | 3 | #import 4 | 5 | @interface ___FILEBASENAMEASIDENTIFIER___ : XCTestCase 6 | 7 | @end 8 | 9 | @implementation ___FILEBASENAMEASIDENTIFIER___ 10 | 11 | - (void)setUp { 12 | [super setUp]; 13 | 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | self.continueAfterFailure = NO; 18 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 19 | [[[XCUIApplication alloc] init] launch]; 20 | 21 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 22 | } 23 | 24 | - (void)tearDown { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /协程 Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /协程 Demo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /协程 Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ZHCoroutine/Coroutine.s: -------------------------------------------------------------------------------- 1 | // 2 | // Coroutine.s 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/27. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | .text 10 | .align 4 11 | .globl _pushCoroutineEnv 12 | .globl _popCoroutineEnv 13 | .globl _getSP 14 | .globl _getFP 15 | 16 | _pushCoroutineEnv: 17 | stp x21, x30, [x0] 18 | mov x21, x0 19 | bl openSVC 20 | mov x0, x21 21 | ldp x21, x30, [x0] 22 | mov x1, sp 23 | stp x19, x20, [x0] 24 | stp x21, x22, [x0, #0x10] 25 | stp x23, x24, [x0, #0x20] 26 | stp x25, x26, [x0, #0x30] 27 | stp x27, x28, [x0, #0x40] 28 | stp x29, x30, [x0, #0x50] 29 | stp x29, x1, [x0, #0x60] 30 | stp d8, d9, [x0, #0x70] 31 | stp d12, d13, [x0, #0x90] 32 | stp d14, d15, [x0, #0xa0] 33 | mov x0, #0x0 34 | ret 35 | 36 | _popCoroutineEnv: 37 | sub sp, sp, #0x10 38 | mov x21, x0 39 | ldr x0, [x21, #0xb0] 40 | str x0, [sp, #0x8] 41 | add x1, sp, #0x8 42 | orr w0, wzr, #0x3 43 | mov x2, #0x0 44 | bl openSVC 45 | mov x0, x21 46 | add sp, sp, #0x10 47 | ldp x19, x20, [x0] 48 | ldp x21, x22, [x0, #0x10] 49 | ldp x23, x24, [x0, #0x20] 50 | ldp x25, x26, [x0, #0x30] 51 | ldp x27, x28, [x0, #0x40] 52 | ldp x29, x30, [x0, #0x50] 53 | ldp x29, x2, [x0, #0x60] 54 | ldp d8, d9, [x0, #0x70] 55 | ldp d10, d11, [x0, #0x80] 56 | ldp d12, d13, [x0, #0x90] 57 | ldp d14, d15, [x0, #0xa0] 58 | mov sp, x2 59 | ret 60 | 61 | _getSP: 62 | mov x0, sp 63 | ret 64 | 65 | _getFP: 66 | mov x0, x29 67 | ret 68 | 69 | openSVC: 70 | mov x16, #0x30 71 | svc #0x80 72 | stp x29, x30, [sp, #-0x10]! 73 | mov x29, sp 74 | mov sp, x29 75 | ldp x29, x30, [sp], #0x10 76 | ret 77 | -------------------------------------------------------------------------------- /协程 Demo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/21. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Coroutine.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | void test1(void) 19 | { 20 | int i = 0; 21 | char array[5000]; 22 | while (1) { 23 | NSLog(@"test1 %d %p", i, array); 24 | i++; 25 | sleep(1); 26 | coroutine_switch(); 27 | } 28 | } 29 | 30 | 31 | void test3(void) 32 | { 33 | int i = 0; 34 | coroutine_create(test1); 35 | while (1) { 36 | NSLog(@"test3 张鸿 %d", i * 10); 37 | i++; 38 | sleep(1); 39 | // coroutine_switch(); 40 | if (i != 10) { 41 | coroutine_switch(); 42 | } else { 43 | coroutine_release(); 44 | } 45 | } 46 | } 47 | 48 | void test2(void) 49 | { 50 | int i = 1; 51 | char array[50]; 52 | while (1) { 53 | NSLog(@"test2 %d %p", i, array); 54 | i++; 55 | sleep(1); 56 | if (i != 10) { 57 | coroutine_switch(); 58 | } else { 59 | coroutine_release(); 60 | } 61 | } 62 | } 63 | 64 | - (void)viewDidLoad { 65 | [super viewDidLoad]; 66 | 67 | // Do any additional setup after loading the view, typically from a nib. 68 | 69 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 70 | coroutine_start(test2); 71 | NSLog(@"协程任务结束"); 72 | }); 73 | 74 | coroutine_start(test3); 75 | 76 | NSLog(@"哈哈哈"); 77 | 78 | } 79 | 80 | - (void)viewWillAppear:(BOOL)animated 81 | { 82 | [super viewWillAppear:animated]; 83 | 84 | NSLog(@"%@", NSStringFromSelector(_cmd)); 85 | } 86 | 87 | 88 | - (void)didReceiveMemoryWarning { 89 | [super didReceiveMemoryWarning]; 90 | // Dispose of any resources that can be recreated. 91 | } 92 | 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /协程 Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /协程 Demo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/21. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "Coroutine.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 21 | return YES; 22 | } 23 | 24 | 25 | - (void)applicationWillResignActive:(UIApplication *)application { 26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 27 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 28 | } 29 | 30 | 31 | - (void)applicationDidEnterBackground:(UIApplication *)application { 32 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application { 38 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | 42 | - (void)applicationDidBecomeActive:(UIApplication *)application { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | 47 | - (void)applicationWillTerminate:(UIApplication *)application { 48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 49 | } 50 | 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /协程 Demo.xcodeproj/xcuserdata/zhanghong.xcuserdatad/xcschemes/ZHCoroutine.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /协程 Demo.xcodeproj/xcuserdata/zhanghong.xcuserdatad/xcschemes/协程 Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 76 | 78 | 84 | 85 | 86 | 87 | 88 | 89 | 95 | 97 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoroutineDemo 2 | 共享栈式协程实现的 Demo 3 | # 简述 4 | 大约在半年以前,我曾经了解过协程的相关实现,也看过腾讯后台开源的协程库`libco`,对其中实现协程相关的汇编有很深的印象(`libco`适配的是 x86 平台)。接受了这样的思想,我在自己的毕业设计中,写出一套单片机汇编实现的协程。也自此之后,就有种念头在 iOS 端实现相关的代码。手机端使用的 CPU 跟单片机相差甚远,虽然我用的单片机也是 ARM 平台的,但和手机端 CPU 相比两者差距确很大,内核微架构差距太大,无从入手。后续突然想起可以使用`setjmp`和`longjmp`这两个函数间跳转函数,我为何不反汇编它,根据其汇编,来实现协程呢? 5 | 最后,我实现了这个想法,先把 Demo 放上来:[协程 Demo](https://github.com/suancaiAmour/CoroutineDemo)。读者可以根据 Demo 和本篇文章一起了解相关内容。 6 | 7 | # 协程的意义 8 | 协程早在 60 年代提出的一种概念,但在后续的发展中,这种理念得不到发展。原因在于 C 语言大行其道,在C 中很忌讳软件开发中,这种无限制的跳转。类似像`goto`语句,几乎编程教科书中都对`goto`语句进行了评判。但就我个人觉得,其实没有必要,像`goto`语句,有其强大之处,在 C 中,函数有很多地方都会走向结束并释放资源,而`goto`语句提供了一个统一地方去释放资源,这也能看到`goto`的作用,因此至今 linux 内核中还有几万个`goto`语句。协程也是如此,但协程更胆大妄为的跳转,早早的跟不上主流。而编程技术发展到今天,很多脚本语言都开始支持了协程,在 JS,lua 和 go 语言都有明显的使用,协程也在实际应用中进一步使用,例如腾讯微信后台协程库`libco`。 9 | 协程允许了一个函数可以跳转到另一个函数当中,执行另一个函数代码,前一个函数的栈是保存下来的,一定时机后也会从其他函数中跳转回来,恢复栈,继续跑下面的代码。这不是普通函数间的调用,协程是直接跳转到另一个栈的函数中去,让 CPU 跑那个函数,那个函数的栈和之前函数的栈根本无任何联系。如何实现,关键就在于栈的保存和当时函数将要跳转的时候,CPU 相关寄存器内容的保存,而`setjmp`和`longjmp`便实现了 CPU 相关寄存器内容的保存,我反汇编也得以实现这一想法(其实直接采用`setjmp`之类也可以实现协程,但后续扩展协程之间交互等,还是需要自己处理汇编来实现)。共享栈式表示在运行中,函数的栈只有一个,在跳转中,前一个函数栈的内容会被保存在堆中,然后要执行的函数栈的内容会被复制到栈中,保持运行。这其中就涉及到很多问题,我在其中也遇到了很多麻烦。这个 Demo 中并不像`libco`中使用`epoll`之类的函数实现 I/O 模型,我这里只是实现了协程间的跳转和相关上下文的切换。 10 | 协程先天性的优势在于处理高 I/O 任务的高效,比线程还轻量级的上下文切换,耗费极小的 CPU 性能,函数栈的保存致使它处理 I/O 任务像是在处理同步任务一样,不必考虑异步编程带来的回调地狱。而它的不足在于它无法处理高 CPU 任务,因为协程任务并不是并发执行的,没有像线程那样的时间片轮转机制。当一条线程执行高计算量的任务时,必然会影响到其他协程任务的执行时间。使用协程也会较占据内存空间,因为协程栈的内容是必须保存在内存中,当成千上万条协程执行时,内存会显的比较有压力,但实际上采用共享栈模式以后,协程的内存耗费量已经大规模下降,至少是可以接受的。`libco`也已经达到千万级别的协程支持了。 11 | 12 | 13 | # ARM 相关寄存器保存的实现 14 | 具体内容在 Demo 的`Coroutine.s`中实现了。 15 | 如果要阅读相关汇编代码,可以先了解一下 ARM64 的寄存器,具体可看[ARMv8-寄存器](http://bdxnote.blog.163.com/blog/static/844423520155913829432),要写 ARM64 汇编,必须要了解[ARM v8指令集(手册)](http://blog.csdn.net/xy010902100449/article/details/51902001)。 16 | 17 | ```ASM 18 | .text 19 | .align 4 20 | .globl _pushCoroutineEnv 21 | .globl _popCoroutineEnv 22 | .globl _getSP 23 | .globl _getFP 24 | 25 | _pushCoroutineEnv: 26 | stp x21, x30, [x0] 27 | mov x21, x0 28 | bl openSVC 29 | mov x0, x21 30 | ldp x21, x30, [x0] 31 | mov x1, sp 32 | stp x19, x20, [x0] 33 | stp x21, x22, [x0, #0x10] 34 | stp x23, x24, [x0, #0x20] 35 | stp x25, x26, [x0, #0x30] 36 | stp x27, x28, [x0, #0x40] 37 | stp x29, x30, [x0, #0x50] 38 | stp x29, x1, [x0, #0x60] 39 | stp d8, d9, [x0, #0x70] 40 | stp d12, d13, [x0, #0x90] 41 | stp d14, d15, [x0, #0xa0] 42 | mov x0, #0x0 43 | ret 44 | 45 | _popCoroutineEnv: 46 | sub sp, sp, #0x10 47 | mov x21, x0 48 | ldr x0, [x21, #0xb0] 49 | str x0, [sp, #0x8] 50 | add x1, sp, #0x8 51 | orr w0, wzr, #0x3 52 | mov x2, #0x0 53 | bl openSVC 54 | mov x0, x21 55 | add sp, sp, #0x10 56 | ldp x19, x20, [x0] 57 | ldp x21, x22, [x0, #0x10] 58 | ldp x23, x24, [x0, #0x20] 59 | ldp x25, x26, [x0, #0x30] 60 | ldp x27, x28, [x0, #0x40] 61 | ldp x29, x30, [x0, #0x50] 62 | ldp x29, x2, [x0, #0x60] 63 | ldp d8, d9, [x0, #0x70] 64 | ldp d10, d11, [x0, #0x80] 65 | ldp d12, d13, [x0, #0x90] 66 | ldp d14, d15, [x0, #0xa0] 67 | mov sp, x2 68 | ret 69 | 70 | _getSP: 71 | mov x0, sp 72 | ret 73 | 74 | _getFP: 75 | mov x0, x29 76 | ret 77 | 78 | openSVC: 79 | mov x16, #0x30 80 | svc #0x80 81 | stp x29, x30, [sp, #-0x10]! 82 | mov x29, sp 83 | mov sp, x29 84 | ldp x29, x30, [sp], #0x10 85 | ret 86 | ``` 87 | 88 | 在`Coroutine.s`实现的内容大体像上面那样(后续版本可能会有迭代,不一定跟上面相似),简单介绍其几个函数的作用: 89 | 90 | _pushCoroutineEnv: 保存调用此函数时,为了后续执行,把 ARM 相关寄存器保存到内存中。 91 | 92 | _popCoroutineEnv: 从内存保存过的 ARM 相关寄存器的内容从新赋值到对应的寄存器内,要注意的是,此时`LR`(即`x30`寄存器)寄存器已经改变,所以当执行到`ret`语句时,函数的执行地址会跳转到新的`LR`所保存的地址上去,也其实就是`_pushCoroutineEnv`的下一语句中。`_pushCoroutineEnv`和`_popCoroutineEnv`是两两相对的。 93 | 94 | _getSP: 获取到栈底寄存器的内容,为后续栈内容的拷贝使用。 95 | 96 | _getFP: 获取到栈帧寄存器内容,主要是为了创建新的协程任务,让新的协程任务的栈可以依靠到触发函数的栈中。 97 | 98 | openSVC: 开启 ARM 芯片的 SVC 模式,也就是超级用户模式, ARM 芯片有五种模式,在不同模式有不同的作用。只有开启了 SVC 模式,我们的代码才能访问到一些特定的寄存器,不在此模式访问了那些寄存器,会出现硬件错误。这是 ARM 芯片硬件实现的权限管理,避免非内核访问到不该访问的内容。所以每次保存寄存器内容和恢复寄存器内容必须要开启 SVC 模式。 99 | 100 | 后续如果要增加协程同步等功能的时候,还会修改这些相关的汇编代码,0.1 版本的协程 Demo 只实现了最基础的功能,连 I/O 模型都没有,所以代码量也并不会很多。 101 | 102 | # Demo 中相关 API 的介绍 103 | 关键函数有 4 个: 104 | 105 | ```c 106 | typedef void (*coroutineTask)(void); 107 | 108 | void coroutine_switch(void); 109 | void coroutine_release(void); 110 | void coroutine_start(coroutineTask entryTask); 111 | void coroutine_create(coroutineTask task); 112 | ``` 113 | 114 | coroutine_start: 开启协程,并启动一个入口`entryTask`。注意当执行到`coroutine_start`函数后面下一语句时,这时协程已经结束了,协程环境也被释放了。 115 | 116 | coroutine_create: 创建一个协程,注意,在未使用`coroutine_start`前是无法创建协程的,相关环境并未创建好,因此,`coroutine_create`会在`entryTask`或者其他协程里面使用,只有协程里面才能创建另一个协程。 117 | 118 | 119 | coroutine_release: 当一个协程要结束时,必须调用`coroutine_release`函数,来释放此条协程的环境,不然,会跳到此条协程第一条代码语句继续执行。 120 | 121 | coroutine_switch: 协程切换,当这条协程需要等待 I/O 的时候,可以切换到另一条协程中,让 CPU 继续执行另一条协程的代码,具体的跳转机制是链表实现的,开发者不必考虑具体会切换到哪一条协程,都是照链表的顺序执行下去的。 122 | 123 | 124 | # 相关 API 的解析 125 | 这里就不贴代码,具体可看文件`Coroutine.c`。 126 | 127 | ## coroutine\_start 128 | 129 | 1. 初始化一下`pthread`相关的东西,确保每条线程之间的协程环境不会杂在一起,这里就体现出面对对象的重要性了,如果使用面对对象根本不会有这种问题,但这里我一开始并没有这样的打算,因为 C API 显的更简洁。 130 | 2. 获取到栈顶和栈底寄存器,必须在这个函数获取,因为这是所有协程的开始点。 131 | 3. 创建空白协程和入口,空白协程用来检测所有协程是否结束任务,如果结束,释放相关资源,跳回线程中继续执行线程代码。 132 | 4. 开启空白协程,执行协程代码。 133 | 134 | ## coroutine\_create 135 | 136 | 1. 获取到协程起始点的栈顶寄存器和栈帧寄存器。 137 | 2. 将栈顶寄存器,栈帧寄存器和`LR`寄存器(task 的地址)相关内容放在链表中。 138 | 139 | ## coroutine\_release 140 | 141 | 在链表中把这个协程的释放标志位打开。 142 | 143 | ## coroutine\_switch 144 | 这个函数是关键。 145 | 146 | 1. 获取到当前执行的协程,将它的栈和相关寄存器的内容更新到链表中。 147 | 2. 从链表中获取到下一协程,如果此协程是要被释放,则释放此协程,再去找寻下一协程,直到找到可执行协程,然后,将可执行协程的栈的内容和相关寄存器内容赋值到栈和寄存器中。 148 | 3. 如此便会执行下一可执行协程代码。 149 | 150 | ## 空白协程 151 | 152 | 检测链表中是否只有自己一个协程,如果是,释放协程环境,否,则切换到下一协程。 153 | 154 | # 总结 155 | 为了实现相关逻辑,实际上也遇到了一些问题,但也让我加深了对 ARM 芯片和栈等的了解。 156 | 比如说,要将堆上的内容复制到栈上去,使用`memcpy`函数是会出问题的,因为`memcpy`也会使用到栈,这样在复制的时候,会把`memcpy`的栈干掉,致使出现问题。后续的解决方案是自己从新实现了一个`memcpy`类似的函数,将要使用的变量放在静态区域,因为栈和堆肯定不会在同一内存区域,不会内存冲突问题,这个函数也好写。但带来的问题是,必须对静态区域加互斥锁,不然在不同线程肯定会出问题,这就造成了性能损耗,当然最好的方法是用汇编实现`memcpy`函数,将相关变量放在寄存器内。 157 | 有想法就要实现,看起来还是很完美的。 158 | -------------------------------------------------------------------------------- /ZHCoroutine/Coroutine.c: -------------------------------------------------------------------------------- 1 | 2 | // Coroutine.c 3 | // 协程 Demo 4 | // 5 | // Created by 张鸿 on 2017/11/28. 6 | // Copyright © 2017年 酸菜Amour. All rights reserved. 7 | // 8 | 9 | #include "Coroutine.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | typedef struct CoroutineUnit 16 | { 17 | int *regEnv; // 存储寄存器内容的缓冲区 大小为 48 18 | void *stack; // 栈 19 | long stackSize; // 栈的大小 20 | int isSwitch:1, isRelease:1; // 标志位 切换标志 释放任务标志 21 | struct CoroutineUnit *next; 22 | }*pCoroutineUnit, coroutineUnit; 23 | 24 | typedef struct ThreadKey 25 | { 26 | pthread_key_t coroutineKey; 27 | pthread_key_t spKey; 28 | pthread_key_t fpKey; 29 | pthread_key_t emptyUnitKey; 30 | pthread_key_t endBufKey; 31 | }*pThreadKey, threadKey; 32 | 33 | typedef struct ThreadInfo 34 | { 35 | pThreadKey *keyArray; 36 | pthread_t *threadArray; 37 | long length; 38 | }*pThreadInfo, threadInfo; 39 | 40 | extern void pushCoroutineEnv(int *regEnv); 41 | extern void popCoroutineEnv(int *regEnv); 42 | extern void *getSP(void); 43 | extern void *getFP(void); 44 | 45 | static threadInfo threadMap; 46 | pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 47 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 48 | 49 | #define ENDFLAG 2 50 | 51 | static pThreadKey getThreadKey(void) 52 | { 53 | pthread_rwlock_rdlock(&rwlock); 54 | for (unsigned long threadId = 0; threadId < threadMap.length; threadId++) { 55 | if (threadMap.threadArray[threadId] == pthread_self()) { 56 | pthread_rwlock_unlock(&rwlock); 57 | return threadMap.keyArray[threadId]; 58 | } 59 | } 60 | pthread_rwlock_unlock(&rwlock); 61 | return NULL; 62 | } 63 | 64 | void emptyTask(void) 65 | { 66 | int array[5000]; 67 | while (1) { 68 | printf("待机任务! %p\r\n", array); 69 | pThreadKey key = getThreadKey(); 70 | pCoroutineUnit pTaskUnit = (pCoroutineUnit)pthread_getspecific(key->coroutineKey); 71 | if (pTaskUnit->next == pTaskUnit) { 72 | pCoroutineUnit __pEmptyUnit = (pCoroutineUnit)pthread_getspecific(key->emptyUnitKey); 73 | if ((*__pEmptyUnit).regEnv) { 74 | free((*__pEmptyUnit).regEnv); 75 | (*__pEmptyUnit).regEnv = NULL; 76 | } 77 | if ((*__pEmptyUnit).stack) { 78 | free((*__pEmptyUnit).stack); 79 | (*__pEmptyUnit).stack = NULL; 80 | } 81 | free(__pEmptyUnit); 82 | pthread_key_delete(key->coroutineKey); 83 | pthread_key_delete(key->spKey); 84 | pthread_key_delete(key->fpKey); 85 | pthread_key_delete(key->emptyUnitKey); 86 | jmp_buf *endBuf = pthread_getspecific(key->endBufKey); 87 | pthread_key_delete(key->endBufKey); 88 | longjmp(*endBuf, ENDFLAG); 89 | } 90 | coroutine_switch(); 91 | } 92 | } 93 | 94 | // 创建协程任务 95 | void coroutine_create(coroutineTask task) 96 | { 97 | pThreadKey key = getThreadKey(); 98 | 99 | void *__fp = pthread_getspecific(key->fpKey); 100 | void *__sp = pthread_getspecific(key->spKey); 101 | 102 | pCoroutineUnit pTaskUnit = (pCoroutineUnit)calloc(1, sizeof(coroutineUnit)); 103 | // 初始化任务的 CPU 寄存器 104 | 105 | (*pTaskUnit).regEnv = (int *)calloc(48, sizeof(int)); 106 | (*pTaskUnit).regEnv[24] = (int)(long)__fp; 107 | (*pTaskUnit).regEnv[25] = (int)(long)((long)__fp >> 32); 108 | (*pTaskUnit).regEnv[22] = (int)(long)task; 109 | (*pTaskUnit).regEnv[23] = (int)(long)((long)task >> 32); 110 | (*pTaskUnit).regEnv[26] = (int)(long)__sp; 111 | (*pTaskUnit).regEnv[27] = (int)(long)((long)__sp >> 32); 112 | 113 | pCoroutineUnit __pEmptyUnit = (pCoroutineUnit)pthread_getspecific(key->emptyUnitKey); 114 | (*pTaskUnit).next = __pEmptyUnit->next; 115 | __pEmptyUnit->next = pTaskUnit; 116 | } 117 | 118 | static pCoroutineUnit findPreCoroutineUnit(pCoroutineUnit currentUnit) 119 | { 120 | pThreadKey key = getThreadKey(); 121 | pCoroutineUnit preUnit = (pCoroutineUnit)pthread_getspecific(key->emptyUnitKey); 122 | while (1) { 123 | if (preUnit->next == currentUnit) { 124 | return preUnit; 125 | } else { 126 | preUnit = preUnit->next; 127 | } 128 | } 129 | } 130 | 131 | // 释放当前协程任务 132 | void coroutine_release(void) 133 | { 134 | pThreadKey key = getThreadKey(); 135 | pCoroutineUnit pTaskUnit = (pCoroutineUnit)pthread_getspecific(key->coroutineKey); 136 | pTaskUnit->isRelease = 1; 137 | coroutine_switch(); 138 | } 139 | 140 | static inline void memcpy2stack(void *dest, void *src, long count) 141 | { 142 | // TODO: 后续可以使用寄存器代替 143 | // 为什么不用 register 144 | // 经测试编译器会对 register 变量进行优化,实际上存储到栈上了。 145 | static char *tmp = NULL; 146 | static char *s = NULL; 147 | static long repeatCount = 0; 148 | tmp = dest; 149 | s = src; 150 | repeatCount = count; 151 | 152 | while (repeatCount--) { 153 | *tmp++ = *s++; 154 | } 155 | } 156 | 157 | // 从当前任务切换到下一任务 158 | void coroutine_switch(void) 159 | { 160 | // 当前任务栈的大小 161 | pThreadKey key = getThreadKey(); 162 | void *__sp = pthread_getspecific(key->spKey); 163 | long stackSize = (long)__sp - (long)getSP(); 164 | 165 | pCoroutineUnit pTaskUnit = (pCoroutineUnit)pthread_getspecific(key->coroutineKey); 166 | (*pTaskUnit).stackSize = stackSize; 167 | 168 | if (pTaskUnit->stack) { 169 | free(pTaskUnit->stack); 170 | pTaskUnit->stack = NULL; 171 | } 172 | 173 | (*pTaskUnit).stack = calloc(1, stackSize); 174 | memcpy((*pTaskUnit).stack, getSP(), (*pTaskUnit).stackSize); 175 | pushCoroutineEnv((*pTaskUnit).regEnv); 176 | 177 | pTaskUnit = (pCoroutineUnit)pthread_getspecific(key->coroutineKey); 178 | if ((*pTaskUnit).isSwitch) { 179 | (*pTaskUnit).isSwitch = 0; // 取消任务的切换状态 180 | return; 181 | } 182 | 183 | pCoroutineUnit pNextTaskUnit = (*pTaskUnit).next; 184 | while (pNextTaskUnit->isRelease) { 185 | pCoroutineUnit preUnit = findPreCoroutineUnit(pNextTaskUnit); 186 | preUnit->next = pNextTaskUnit->next; 187 | 188 | if ((*pNextTaskUnit).regEnv) { 189 | free((*pNextTaskUnit).regEnv); 190 | (*pNextTaskUnit).regEnv = NULL; 191 | } 192 | if ((*pNextTaskUnit).stack) { 193 | free((*pNextTaskUnit).stack); 194 | (*pNextTaskUnit).stack = NULL; 195 | } 196 | free(pNextTaskUnit); 197 | pNextTaskUnit = preUnit->next; 198 | } 199 | 200 | pthread_setspecific(key->coroutineKey, pNextTaskUnit); 201 | if ((*pNextTaskUnit).stack) { 202 | (*pNextTaskUnit).isSwitch = 1; //下一个是切换过来 203 | pthread_mutex_lock(&mutex); 204 | memcpy2stack((void *)((long)__sp - (*pNextTaskUnit).stackSize),(*pNextTaskUnit).stack, (*pNextTaskUnit).stackSize); 205 | pthread_mutex_unlock(&mutex); 206 | key = getThreadKey(); 207 | pNextTaskUnit = (pCoroutineUnit)pthread_getspecific(key->coroutineKey); 208 | } 209 | popCoroutineEnv((*pNextTaskUnit).regEnv); 210 | } 211 | 212 | pthread_once_t once_control = PTHREAD_ONCE_INIT; 213 | 214 | static void initLock(void) 215 | { 216 | pthread_rwlock_init(&rwlock, NULL); 217 | pthread_mutex_init(&mutex,NULL); 218 | } 219 | 220 | void coroutine_start(coroutineTask entryTask) 221 | { 222 | pthread_once(&once_control, initLock); 223 | 224 | pThreadKey key = getThreadKey(); 225 | 226 | if (!key) { 227 | key = (pThreadKey)calloc(1, sizeof(threadKey)); 228 | pthread_key_create(&key->spKey, NULL); 229 | pthread_key_create(&key->fpKey, NULL); 230 | pthread_key_create(&key->emptyUnitKey, NULL); 231 | 232 | void *__sp = getSP(); // 栈底寄存器 233 | void *__fp = getFP(); // 栈帧寄存器 234 | pthread_setspecific(key->spKey, __sp); 235 | pthread_setspecific(key->fpKey, __fp); 236 | 237 | pCoroutineUnit __pEmptyUnit = (pCoroutineUnit)calloc(1, sizeof(coroutineUnit)); 238 | // 初始化任务的 CPU 寄存器 239 | (*__pEmptyUnit).regEnv = (int *)calloc(48, sizeof(int)); 240 | (*__pEmptyUnit).regEnv[24] = (int)(long)__fp; 241 | (*__pEmptyUnit).regEnv[25] = (int)(long)((long)__fp >> 32); 242 | (*__pEmptyUnit).regEnv[22] = (int)(long)emptyTask; 243 | (*__pEmptyUnit).regEnv[23] = (int)(long)((long)emptyTask >> 32); 244 | (*__pEmptyUnit).regEnv[26] = (int)(long)__sp; 245 | (*__pEmptyUnit).regEnv[27] = (int)(long)((long)__sp >> 32); 246 | (*__pEmptyUnit).next = __pEmptyUnit; 247 | pthread_setspecific(key->emptyUnitKey, __pEmptyUnit); 248 | 249 | pthread_rwlock_wrlock(&rwlock); 250 | long length = threadMap.length++; 251 | pThreadKey *keyArray = (pThreadKey *)calloc(threadMap.length, sizeof(pThreadKey)); 252 | pthread_t *threadArray = (pthread_t *)calloc(threadMap.length, sizeof(pthread_t)); 253 | memcpy(keyArray, threadMap.keyArray, length * sizeof(pThreadKey)); 254 | memcpy(threadArray, threadMap.threadArray, length * sizeof(pthread_t)); 255 | keyArray[length] = key; 256 | threadArray[length] = pthread_self(); 257 | free(threadMap.keyArray); 258 | free(threadMap.threadArray); 259 | threadMap.keyArray = keyArray; 260 | threadMap.threadArray = threadArray; 261 | pthread_rwlock_unlock(&rwlock); 262 | } 263 | 264 | coroutine_create(entryTask); 265 | 266 | pthread_key_create(&key->coroutineKey, NULL); 267 | pthread_key_create(&key->endBufKey, NULL); 268 | pCoroutineUnit __pEmptyUnit = (pCoroutineUnit)pthread_getspecific(key->emptyUnitKey); 269 | pthread_setspecific(key->coroutineKey, __pEmptyUnit); 270 | jmp_buf *endBuf = calloc(1, sizeof(jmp_buf)); 271 | pthread_setspecific(key->endBufKey, endBuf); 272 | int endFlag = setjmp(*endBuf); 273 | if (endFlag != ENDFLAG) { 274 | popCoroutineEnv(__pEmptyUnit->regEnv); 275 | } 276 | free(endBuf); 277 | } 278 | -------------------------------------------------------------------------------- /协程 Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FC2789AD1FC348C5006039B6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FC2789AC1FC348C5006039B6 /* AppDelegate.m */; }; 11 | FC2789B01FC348C5006039B6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FC2789AF1FC348C5006039B6 /* ViewController.m */; }; 12 | FC2789B31FC348C5006039B6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC2789B11FC348C5006039B6 /* Main.storyboard */; }; 13 | FC2789B51FC348C5006039B6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FC2789B41FC348C5006039B6 /* Assets.xcassets */; }; 14 | FC2789B81FC348C5006039B6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FC2789B61FC348C5006039B6 /* LaunchScreen.storyboard */; }; 15 | FC2789BB1FC348C5006039B6 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = FC2789BA1FC348C5006039B6 /* main.m */; }; 16 | FC2789C51FC348C5006039B6 /* ___DemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC2789C41FC348C5006039B6 /* ___DemoTests.m */; }; 17 | FC2789D01FC348C5006039B6 /* ___DemoUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC2789CF1FC348C5006039B6 /* ___DemoUITests.m */; }; 18 | FCCEA8FA1FDF509800182EA7 /* Coroutine.s in Sources */ = {isa = PBXBuildFile; fileRef = FCCEA8F71FDF509800182EA7 /* Coroutine.s */; }; 19 | FCCEA8FB1FDF509800182EA7 /* Coroutine.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCEA8F91FDF509800182EA7 /* Coroutine.c */; }; 20 | FCCEA8FD1FDF51DB00182EA7 /* libZHCoroutine.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FCCEA8EE1FDF505A00182EA7 /* libZHCoroutine.a */; }; 21 | FCCEA8FF1FDF51F300182EA7 /* Coroutine.h in Headers */ = {isa = PBXBuildFile; fileRef = FCCEA8F81FDF509800182EA7 /* Coroutine.h */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | FC2789C11FC348C5006039B6 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = FC2789A01FC348C5006039B6 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = FC2789A71FC348C5006039B6; 30 | remoteInfo = "协程 Demo"; 31 | }; 32 | FC2789CC1FC348C5006039B6 /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = FC2789A01FC348C5006039B6 /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = FC2789A71FC348C5006039B6; 37 | remoteInfo = "协程 Demo"; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXCopyFilesBuildPhase section */ 42 | FCCEA8EC1FDF505A00182EA7 /* CopyFiles */ = { 43 | isa = PBXCopyFilesBuildPhase; 44 | buildActionMask = 2147483647; 45 | dstPath = "include/$(PRODUCT_NAME)"; 46 | dstSubfolderSpec = 16; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXCopyFilesBuildPhase section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | FC2789A81FC348C5006039B6 /* 协程 Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "协程 Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | FC2789AB1FC348C5006039B6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | FC2789AC1FC348C5006039B6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | FC2789AE1FC348C5006039B6 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 58 | FC2789AF1FC348C5006039B6 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 59 | FC2789B21FC348C5006039B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 60 | FC2789B41FC348C5006039B6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 61 | FC2789B71FC348C5006039B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 62 | FC2789B91FC348C5006039B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | FC2789BA1FC348C5006039B6 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 64 | FC2789C01FC348C5006039B6 /* 协程 DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "协程 DemoTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | FC2789C41FC348C5006039B6 /* ___DemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "___DemoTests.m"; sourceTree = ""; }; 66 | FC2789C61FC348C5006039B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 67 | FC2789CB1FC348C5006039B6 /* 协程 DemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "协程 DemoUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | FC2789CF1FC348C5006039B6 /* ___DemoUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "___DemoUITests.m"; sourceTree = ""; }; 69 | FC2789D11FC348C5006039B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | FCCEA8EE1FDF505A00182EA7 /* libZHCoroutine.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libZHCoroutine.a; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | FCCEA8F71FDF509800182EA7 /* Coroutine.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = Coroutine.s; sourceTree = ""; }; 72 | FCCEA8F81FDF509800182EA7 /* Coroutine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Coroutine.h; sourceTree = ""; }; 73 | FCCEA8F91FDF509800182EA7 /* Coroutine.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = Coroutine.c; sourceTree = ""; }; 74 | /* End PBXFileReference section */ 75 | 76 | /* Begin PBXFrameworksBuildPhase section */ 77 | FC2789A51FC348C5006039B6 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | FCCEA8FD1FDF51DB00182EA7 /* libZHCoroutine.a in Frameworks */, 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | FC2789BD1FC348C5006039B6 /* Frameworks */ = { 86 | isa = PBXFrameworksBuildPhase; 87 | buildActionMask = 2147483647; 88 | files = ( 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | FC2789C81FC348C5006039B6 /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | FCCEA8EB1FDF505A00182EA7 /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | ); 104 | runOnlyForDeploymentPostprocessing = 0; 105 | }; 106 | /* End PBXFrameworksBuildPhase section */ 107 | 108 | /* Begin PBXGroup section */ 109 | FC27899F1FC348C5006039B6 = { 110 | isa = PBXGroup; 111 | children = ( 112 | FC2789AA1FC348C5006039B6 /* 协程 Demo */, 113 | FC2789C31FC348C5006039B6 /* 协程 DemoTests */, 114 | FC2789CE1FC348C5006039B6 /* 协程 DemoUITests */, 115 | FCCEA8EF1FDF505A00182EA7 /* ZHCoroutine */, 116 | FC2789A91FC348C5006039B6 /* Products */, 117 | FCCEA8FC1FDF51DB00182EA7 /* Frameworks */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | FC2789A91FC348C5006039B6 /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | FC2789A81FC348C5006039B6 /* 协程 Demo.app */, 125 | FC2789C01FC348C5006039B6 /* 协程 DemoTests.xctest */, 126 | FC2789CB1FC348C5006039B6 /* 协程 DemoUITests.xctest */, 127 | FCCEA8EE1FDF505A00182EA7 /* libZHCoroutine.a */, 128 | ); 129 | name = Products; 130 | sourceTree = ""; 131 | }; 132 | FC2789AA1FC348C5006039B6 /* 协程 Demo */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | FC2789AB1FC348C5006039B6 /* AppDelegate.h */, 136 | FC2789AC1FC348C5006039B6 /* AppDelegate.m */, 137 | FC2789AE1FC348C5006039B6 /* ViewController.h */, 138 | FC2789AF1FC348C5006039B6 /* ViewController.m */, 139 | FC2789B11FC348C5006039B6 /* Main.storyboard */, 140 | FC2789B41FC348C5006039B6 /* Assets.xcassets */, 141 | FC2789B61FC348C5006039B6 /* LaunchScreen.storyboard */, 142 | FC2789B91FC348C5006039B6 /* Info.plist */, 143 | FC2789BA1FC348C5006039B6 /* main.m */, 144 | ); 145 | path = "协程 Demo"; 146 | sourceTree = ""; 147 | }; 148 | FC2789C31FC348C5006039B6 /* 协程 DemoTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | FC2789C41FC348C5006039B6 /* ___DemoTests.m */, 152 | FC2789C61FC348C5006039B6 /* Info.plist */, 153 | ); 154 | path = "协程 DemoTests"; 155 | sourceTree = ""; 156 | }; 157 | FC2789CE1FC348C5006039B6 /* 协程 DemoUITests */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | FC2789CF1FC348C5006039B6 /* ___DemoUITests.m */, 161 | FC2789D11FC348C5006039B6 /* Info.plist */, 162 | ); 163 | path = "协程 DemoUITests"; 164 | sourceTree = ""; 165 | }; 166 | FCCEA8EF1FDF505A00182EA7 /* ZHCoroutine */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | FCCEA8F91FDF509800182EA7 /* Coroutine.c */, 170 | FCCEA8F81FDF509800182EA7 /* Coroutine.h */, 171 | FCCEA8F71FDF509800182EA7 /* Coroutine.s */, 172 | ); 173 | path = ZHCoroutine; 174 | sourceTree = ""; 175 | }; 176 | FCCEA8FC1FDF51DB00182EA7 /* Frameworks */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | ); 180 | name = Frameworks; 181 | sourceTree = ""; 182 | }; 183 | /* End PBXGroup section */ 184 | 185 | /* Begin PBXHeadersBuildPhase section */ 186 | FCCEA8FE1FDF51EB00182EA7 /* Headers */ = { 187 | isa = PBXHeadersBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | FCCEA8FF1FDF51F300182EA7 /* Coroutine.h in Headers */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXHeadersBuildPhase section */ 195 | 196 | /* Begin PBXNativeTarget section */ 197 | FC2789A71FC348C5006039B6 /* 协程 Demo */ = { 198 | isa = PBXNativeTarget; 199 | buildConfigurationList = FC2789D41FC348C6006039B6 /* Build configuration list for PBXNativeTarget "协程 Demo" */; 200 | buildPhases = ( 201 | FC2789A41FC348C5006039B6 /* Sources */, 202 | FC2789A51FC348C5006039B6 /* Frameworks */, 203 | FC2789A61FC348C5006039B6 /* Resources */, 204 | FCCEA8FE1FDF51EB00182EA7 /* Headers */, 205 | ); 206 | buildRules = ( 207 | ); 208 | dependencies = ( 209 | ); 210 | name = "协程 Demo"; 211 | productName = "协程 Demo"; 212 | productReference = FC2789A81FC348C5006039B6 /* 协程 Demo.app */; 213 | productType = "com.apple.product-type.application"; 214 | }; 215 | FC2789BF1FC348C5006039B6 /* 协程 DemoTests */ = { 216 | isa = PBXNativeTarget; 217 | buildConfigurationList = FC2789D71FC348C6006039B6 /* Build configuration list for PBXNativeTarget "协程 DemoTests" */; 218 | buildPhases = ( 219 | FC2789BC1FC348C5006039B6 /* Sources */, 220 | FC2789BD1FC348C5006039B6 /* Frameworks */, 221 | FC2789BE1FC348C5006039B6 /* Resources */, 222 | ); 223 | buildRules = ( 224 | ); 225 | dependencies = ( 226 | FC2789C21FC348C5006039B6 /* PBXTargetDependency */, 227 | ); 228 | name = "协程 DemoTests"; 229 | productName = "协程 DemoTests"; 230 | productReference = FC2789C01FC348C5006039B6 /* 协程 DemoTests.xctest */; 231 | productType = "com.apple.product-type.bundle.unit-test"; 232 | }; 233 | FC2789CA1FC348C5006039B6 /* 协程 DemoUITests */ = { 234 | isa = PBXNativeTarget; 235 | buildConfigurationList = FC2789DA1FC348C6006039B6 /* Build configuration list for PBXNativeTarget "协程 DemoUITests" */; 236 | buildPhases = ( 237 | FC2789C71FC348C5006039B6 /* Sources */, 238 | FC2789C81FC348C5006039B6 /* Frameworks */, 239 | FC2789C91FC348C5006039B6 /* Resources */, 240 | ); 241 | buildRules = ( 242 | ); 243 | dependencies = ( 244 | FC2789CD1FC348C5006039B6 /* PBXTargetDependency */, 245 | ); 246 | name = "协程 DemoUITests"; 247 | productName = "协程 DemoUITests"; 248 | productReference = FC2789CB1FC348C5006039B6 /* 协程 DemoUITests.xctest */; 249 | productType = "com.apple.product-type.bundle.ui-testing"; 250 | }; 251 | FCCEA8ED1FDF505A00182EA7 /* ZHCoroutine */ = { 252 | isa = PBXNativeTarget; 253 | buildConfigurationList = FCCEA8F41FDF505A00182EA7 /* Build configuration list for PBXNativeTarget "ZHCoroutine" */; 254 | buildPhases = ( 255 | FCCEA8EA1FDF505A00182EA7 /* Sources */, 256 | FCCEA8EB1FDF505A00182EA7 /* Frameworks */, 257 | FCCEA8EC1FDF505A00182EA7 /* CopyFiles */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | ); 263 | name = ZHCoroutine; 264 | productName = ZHCoroutine; 265 | productReference = FCCEA8EE1FDF505A00182EA7 /* libZHCoroutine.a */; 266 | productType = "com.apple.product-type.library.static"; 267 | }; 268 | /* End PBXNativeTarget section */ 269 | 270 | /* Begin PBXProject section */ 271 | FC2789A01FC348C5006039B6 /* Project object */ = { 272 | isa = PBXProject; 273 | attributes = { 274 | LastUpgradeCheck = 0900; 275 | ORGANIZATIONNAME = "酸菜Amour"; 276 | TargetAttributes = { 277 | FC2789A71FC348C5006039B6 = { 278 | CreatedOnToolsVersion = 9.0; 279 | ProvisioningStyle = Automatic; 280 | }; 281 | FC2789BF1FC348C5006039B6 = { 282 | CreatedOnToolsVersion = 9.0; 283 | ProvisioningStyle = Automatic; 284 | TestTargetID = FC2789A71FC348C5006039B6; 285 | }; 286 | FC2789CA1FC348C5006039B6 = { 287 | CreatedOnToolsVersion = 9.0; 288 | ProvisioningStyle = Automatic; 289 | TestTargetID = FC2789A71FC348C5006039B6; 290 | }; 291 | FCCEA8ED1FDF505A00182EA7 = { 292 | CreatedOnToolsVersion = 9.0; 293 | ProvisioningStyle = Automatic; 294 | }; 295 | }; 296 | }; 297 | buildConfigurationList = FC2789A31FC348C5006039B6 /* Build configuration list for PBXProject "协程 Demo" */; 298 | compatibilityVersion = "Xcode 8.0"; 299 | developmentRegion = en; 300 | hasScannedForEncodings = 0; 301 | knownRegions = ( 302 | en, 303 | Base, 304 | ); 305 | mainGroup = FC27899F1FC348C5006039B6; 306 | productRefGroup = FC2789A91FC348C5006039B6 /* Products */; 307 | projectDirPath = ""; 308 | projectRoot = ""; 309 | targets = ( 310 | FC2789A71FC348C5006039B6 /* 协程 Demo */, 311 | FC2789BF1FC348C5006039B6 /* 协程 DemoTests */, 312 | FC2789CA1FC348C5006039B6 /* 协程 DemoUITests */, 313 | FCCEA8ED1FDF505A00182EA7 /* ZHCoroutine */, 314 | ); 315 | }; 316 | /* End PBXProject section */ 317 | 318 | /* Begin PBXResourcesBuildPhase section */ 319 | FC2789A61FC348C5006039B6 /* Resources */ = { 320 | isa = PBXResourcesBuildPhase; 321 | buildActionMask = 2147483647; 322 | files = ( 323 | FC2789B81FC348C5006039B6 /* LaunchScreen.storyboard in Resources */, 324 | FC2789B51FC348C5006039B6 /* Assets.xcassets in Resources */, 325 | FC2789B31FC348C5006039B6 /* Main.storyboard in Resources */, 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | }; 329 | FC2789BE1FC348C5006039B6 /* Resources */ = { 330 | isa = PBXResourcesBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | FC2789C91FC348C5006039B6 /* Resources */ = { 337 | isa = PBXResourcesBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | /* End PBXResourcesBuildPhase section */ 344 | 345 | /* Begin PBXSourcesBuildPhase section */ 346 | FC2789A41FC348C5006039B6 /* Sources */ = { 347 | isa = PBXSourcesBuildPhase; 348 | buildActionMask = 2147483647; 349 | files = ( 350 | FC2789B01FC348C5006039B6 /* ViewController.m in Sources */, 351 | FC2789BB1FC348C5006039B6 /* main.m in Sources */, 352 | FC2789AD1FC348C5006039B6 /* AppDelegate.m in Sources */, 353 | ); 354 | runOnlyForDeploymentPostprocessing = 0; 355 | }; 356 | FC2789BC1FC348C5006039B6 /* Sources */ = { 357 | isa = PBXSourcesBuildPhase; 358 | buildActionMask = 2147483647; 359 | files = ( 360 | FC2789C51FC348C5006039B6 /* ___DemoTests.m in Sources */, 361 | ); 362 | runOnlyForDeploymentPostprocessing = 0; 363 | }; 364 | FC2789C71FC348C5006039B6 /* Sources */ = { 365 | isa = PBXSourcesBuildPhase; 366 | buildActionMask = 2147483647; 367 | files = ( 368 | FC2789D01FC348C5006039B6 /* ___DemoUITests.m in Sources */, 369 | ); 370 | runOnlyForDeploymentPostprocessing = 0; 371 | }; 372 | FCCEA8EA1FDF505A00182EA7 /* Sources */ = { 373 | isa = PBXSourcesBuildPhase; 374 | buildActionMask = 2147483647; 375 | files = ( 376 | FCCEA8FB1FDF509800182EA7 /* Coroutine.c in Sources */, 377 | FCCEA8FA1FDF509800182EA7 /* Coroutine.s in Sources */, 378 | ); 379 | runOnlyForDeploymentPostprocessing = 0; 380 | }; 381 | /* End PBXSourcesBuildPhase section */ 382 | 383 | /* Begin PBXTargetDependency section */ 384 | FC2789C21FC348C5006039B6 /* PBXTargetDependency */ = { 385 | isa = PBXTargetDependency; 386 | target = FC2789A71FC348C5006039B6 /* 协程 Demo */; 387 | targetProxy = FC2789C11FC348C5006039B6 /* PBXContainerItemProxy */; 388 | }; 389 | FC2789CD1FC348C5006039B6 /* PBXTargetDependency */ = { 390 | isa = PBXTargetDependency; 391 | target = FC2789A71FC348C5006039B6 /* 协程 Demo */; 392 | targetProxy = FC2789CC1FC348C5006039B6 /* PBXContainerItemProxy */; 393 | }; 394 | /* End PBXTargetDependency section */ 395 | 396 | /* Begin PBXVariantGroup section */ 397 | FC2789B11FC348C5006039B6 /* Main.storyboard */ = { 398 | isa = PBXVariantGroup; 399 | children = ( 400 | FC2789B21FC348C5006039B6 /* Base */, 401 | ); 402 | name = Main.storyboard; 403 | sourceTree = ""; 404 | }; 405 | FC2789B61FC348C5006039B6 /* LaunchScreen.storyboard */ = { 406 | isa = PBXVariantGroup; 407 | children = ( 408 | FC2789B71FC348C5006039B6 /* Base */, 409 | ); 410 | name = LaunchScreen.storyboard; 411 | sourceTree = ""; 412 | }; 413 | /* End PBXVariantGroup section */ 414 | 415 | /* Begin XCBuildConfiguration section */ 416 | FC2789D21FC348C6006039B6 /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | buildSettings = { 419 | ALWAYS_SEARCH_USER_PATHS = NO; 420 | CLANG_ANALYZER_NONNULL = YES; 421 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 422 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 423 | CLANG_CXX_LIBRARY = "libc++"; 424 | CLANG_ENABLE_MODULES = YES; 425 | CLANG_ENABLE_OBJC_ARC = YES; 426 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 427 | CLANG_WARN_BOOL_CONVERSION = YES; 428 | CLANG_WARN_COMMA = YES; 429 | CLANG_WARN_CONSTANT_CONVERSION = YES; 430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 431 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 432 | CLANG_WARN_EMPTY_BODY = YES; 433 | CLANG_WARN_ENUM_CONVERSION = YES; 434 | CLANG_WARN_INFINITE_RECURSION = YES; 435 | CLANG_WARN_INT_CONVERSION = YES; 436 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 437 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 439 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 440 | CLANG_WARN_STRICT_PROTOTYPES = YES; 441 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 442 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 443 | CLANG_WARN_UNREACHABLE_CODE = YES; 444 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 445 | CODE_SIGN_IDENTITY = "iPhone Developer"; 446 | COPY_PHASE_STRIP = NO; 447 | DEBUG_INFORMATION_FORMAT = dwarf; 448 | ENABLE_STRICT_OBJC_MSGSEND = YES; 449 | ENABLE_TESTABILITY = YES; 450 | GCC_C_LANGUAGE_STANDARD = gnu11; 451 | GCC_DYNAMIC_NO_PIC = NO; 452 | GCC_NO_COMMON_BLOCKS = YES; 453 | GCC_OPTIMIZATION_LEVEL = 0; 454 | GCC_PREPROCESSOR_DEFINITIONS = ( 455 | "DEBUG=1", 456 | "$(inherited)", 457 | ); 458 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 459 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 460 | GCC_WARN_UNDECLARED_SELECTOR = YES; 461 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 462 | GCC_WARN_UNUSED_FUNCTION = YES; 463 | GCC_WARN_UNUSED_VARIABLE = YES; 464 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 465 | MTL_ENABLE_DEBUG_INFO = YES; 466 | ONLY_ACTIVE_ARCH = YES; 467 | SDKROOT = iphoneos; 468 | }; 469 | name = Debug; 470 | }; 471 | FC2789D31FC348C6006039B6 /* Release */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | ALWAYS_SEARCH_USER_PATHS = NO; 475 | CLANG_ANALYZER_NONNULL = YES; 476 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 477 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 478 | CLANG_CXX_LIBRARY = "libc++"; 479 | CLANG_ENABLE_MODULES = YES; 480 | CLANG_ENABLE_OBJC_ARC = YES; 481 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 482 | CLANG_WARN_BOOL_CONVERSION = YES; 483 | CLANG_WARN_COMMA = YES; 484 | CLANG_WARN_CONSTANT_CONVERSION = YES; 485 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 486 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 487 | CLANG_WARN_EMPTY_BODY = YES; 488 | CLANG_WARN_ENUM_CONVERSION = YES; 489 | CLANG_WARN_INFINITE_RECURSION = YES; 490 | CLANG_WARN_INT_CONVERSION = YES; 491 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 492 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 493 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 494 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 495 | CLANG_WARN_STRICT_PROTOTYPES = YES; 496 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 497 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 498 | CLANG_WARN_UNREACHABLE_CODE = YES; 499 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 500 | CODE_SIGN_IDENTITY = "iPhone Developer"; 501 | COPY_PHASE_STRIP = NO; 502 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 503 | ENABLE_NS_ASSERTIONS = NO; 504 | ENABLE_STRICT_OBJC_MSGSEND = YES; 505 | GCC_C_LANGUAGE_STANDARD = gnu11; 506 | GCC_NO_COMMON_BLOCKS = YES; 507 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 508 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 509 | GCC_WARN_UNDECLARED_SELECTOR = YES; 510 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 511 | GCC_WARN_UNUSED_FUNCTION = YES; 512 | GCC_WARN_UNUSED_VARIABLE = YES; 513 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 514 | MTL_ENABLE_DEBUG_INFO = NO; 515 | SDKROOT = iphoneos; 516 | VALIDATE_PRODUCT = YES; 517 | }; 518 | name = Release; 519 | }; 520 | FC2789D51FC348C6006039B6 /* Debug */ = { 521 | isa = XCBuildConfiguration; 522 | buildSettings = { 523 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 524 | CODE_SIGN_STYLE = Automatic; 525 | DEVELOPMENT_TEAM = GQSG2AGKX3; 526 | INFOPLIST_FILE = "协程 Demo/Info.plist"; 527 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 528 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 529 | PRODUCT_BUNDLE_IDENTIFIER = "zh.---Demo"; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | TARGETED_DEVICE_FAMILY = 1; 532 | }; 533 | name = Debug; 534 | }; 535 | FC2789D61FC348C6006039B6 /* Release */ = { 536 | isa = XCBuildConfiguration; 537 | buildSettings = { 538 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 539 | CODE_SIGN_STYLE = Automatic; 540 | DEVELOPMENT_TEAM = GQSG2AGKX3; 541 | INFOPLIST_FILE = "协程 Demo/Info.plist"; 542 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 543 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 544 | PRODUCT_BUNDLE_IDENTIFIER = "zh.---Demo"; 545 | PRODUCT_NAME = "$(TARGET_NAME)"; 546 | TARGETED_DEVICE_FAMILY = 1; 547 | }; 548 | name = Release; 549 | }; 550 | FC2789D81FC348C6006039B6 /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | BUNDLE_LOADER = "$(TEST_HOST)"; 554 | CODE_SIGN_STYLE = Automatic; 555 | INFOPLIST_FILE = "协程 DemoTests/Info.plist"; 556 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 557 | PRODUCT_BUNDLE_IDENTIFIER = "zh.---DemoTests"; 558 | PRODUCT_NAME = "$(TARGET_NAME)"; 559 | TARGETED_DEVICE_FAMILY = "1,2"; 560 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/协程 Demo.app/协程 Demo"; 561 | }; 562 | name = Debug; 563 | }; 564 | FC2789D91FC348C6006039B6 /* Release */ = { 565 | isa = XCBuildConfiguration; 566 | buildSettings = { 567 | BUNDLE_LOADER = "$(TEST_HOST)"; 568 | CODE_SIGN_STYLE = Automatic; 569 | INFOPLIST_FILE = "协程 DemoTests/Info.plist"; 570 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 571 | PRODUCT_BUNDLE_IDENTIFIER = "zh.---DemoTests"; 572 | PRODUCT_NAME = "$(TARGET_NAME)"; 573 | TARGETED_DEVICE_FAMILY = "1,2"; 574 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/协程 Demo.app/协程 Demo"; 575 | }; 576 | name = Release; 577 | }; 578 | FC2789DB1FC348C6006039B6 /* Debug */ = { 579 | isa = XCBuildConfiguration; 580 | buildSettings = { 581 | CODE_SIGN_STYLE = Automatic; 582 | INFOPLIST_FILE = "协程 DemoUITests/Info.plist"; 583 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 584 | PRODUCT_BUNDLE_IDENTIFIER = "zh.---DemoUITests"; 585 | PRODUCT_NAME = "$(TARGET_NAME)"; 586 | TARGETED_DEVICE_FAMILY = "1,2"; 587 | TEST_TARGET_NAME = "协程 Demo"; 588 | }; 589 | name = Debug; 590 | }; 591 | FC2789DC1FC348C6006039B6 /* Release */ = { 592 | isa = XCBuildConfiguration; 593 | buildSettings = { 594 | CODE_SIGN_STYLE = Automatic; 595 | INFOPLIST_FILE = "协程 DemoUITests/Info.plist"; 596 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 597 | PRODUCT_BUNDLE_IDENTIFIER = "zh.---DemoUITests"; 598 | PRODUCT_NAME = "$(TARGET_NAME)"; 599 | TARGETED_DEVICE_FAMILY = "1,2"; 600 | TEST_TARGET_NAME = "协程 Demo"; 601 | }; 602 | name = Release; 603 | }; 604 | FCCEA8F51FDF505A00182EA7 /* Debug */ = { 605 | isa = XCBuildConfiguration; 606 | buildSettings = { 607 | CODE_SIGN_STYLE = Automatic; 608 | OTHER_LDFLAGS = "-ObjC"; 609 | PRODUCT_NAME = "$(TARGET_NAME)"; 610 | SKIP_INSTALL = YES; 611 | TARGETED_DEVICE_FAMILY = "1,2"; 612 | }; 613 | name = Debug; 614 | }; 615 | FCCEA8F61FDF505A00182EA7 /* Release */ = { 616 | isa = XCBuildConfiguration; 617 | buildSettings = { 618 | CODE_SIGN_STYLE = Automatic; 619 | OTHER_LDFLAGS = "-ObjC"; 620 | PRODUCT_NAME = "$(TARGET_NAME)"; 621 | SKIP_INSTALL = YES; 622 | TARGETED_DEVICE_FAMILY = "1,2"; 623 | }; 624 | name = Release; 625 | }; 626 | /* End XCBuildConfiguration section */ 627 | 628 | /* Begin XCConfigurationList section */ 629 | FC2789A31FC348C5006039B6 /* Build configuration list for PBXProject "协程 Demo" */ = { 630 | isa = XCConfigurationList; 631 | buildConfigurations = ( 632 | FC2789D21FC348C6006039B6 /* Debug */, 633 | FC2789D31FC348C6006039B6 /* Release */, 634 | ); 635 | defaultConfigurationIsVisible = 0; 636 | defaultConfigurationName = Release; 637 | }; 638 | FC2789D41FC348C6006039B6 /* Build configuration list for PBXNativeTarget "协程 Demo" */ = { 639 | isa = XCConfigurationList; 640 | buildConfigurations = ( 641 | FC2789D51FC348C6006039B6 /* Debug */, 642 | FC2789D61FC348C6006039B6 /* Release */, 643 | ); 644 | defaultConfigurationIsVisible = 0; 645 | defaultConfigurationName = Release; 646 | }; 647 | FC2789D71FC348C6006039B6 /* Build configuration list for PBXNativeTarget "协程 DemoTests" */ = { 648 | isa = XCConfigurationList; 649 | buildConfigurations = ( 650 | FC2789D81FC348C6006039B6 /* Debug */, 651 | FC2789D91FC348C6006039B6 /* Release */, 652 | ); 653 | defaultConfigurationIsVisible = 0; 654 | defaultConfigurationName = Release; 655 | }; 656 | FC2789DA1FC348C6006039B6 /* Build configuration list for PBXNativeTarget "协程 DemoUITests" */ = { 657 | isa = XCConfigurationList; 658 | buildConfigurations = ( 659 | FC2789DB1FC348C6006039B6 /* Debug */, 660 | FC2789DC1FC348C6006039B6 /* Release */, 661 | ); 662 | defaultConfigurationIsVisible = 0; 663 | defaultConfigurationName = Release; 664 | }; 665 | FCCEA8F41FDF505A00182EA7 /* Build configuration list for PBXNativeTarget "ZHCoroutine" */ = { 666 | isa = XCConfigurationList; 667 | buildConfigurations = ( 668 | FCCEA8F51FDF505A00182EA7 /* Debug */, 669 | FCCEA8F61FDF505A00182EA7 /* Release */, 670 | ); 671 | defaultConfigurationIsVisible = 0; 672 | defaultConfigurationName = Release; 673 | }; 674 | /* End XCConfigurationList section */ 675 | }; 676 | rootObject = FC2789A01FC348C5006039B6 /* Project object */; 677 | } 678 | --------------------------------------------------------------------------------