├── .gitignore ├── LICENSE ├── README.md ├── RJIterator.podspec ├── RJIterator.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── RJIterator ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets └── AppIcon.appiconset │ └── Contents.json ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Class ├── RJAsyncClosureCaller.swift ├── RJIterator.h └── RJIterator.m ├── Info.plist ├── RJIterator-Bridging-Header.h ├── Tests.h ├── Tests.m ├── TestsSwift.swift ├── UserInfoViewController.h ├── UserInfoViewController.m ├── ViewController.h ├── ViewController.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | *.xcodeproj/**/xcuserdata/ 2 | *.xcscmblueprint 3 | /Carthage 4 | /Cartfile.resolved 5 | /.build 6 | .DS_Store 7 | DerivedData 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 renjinkui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 生成器与迭代器是ES6和Python的重要概念,初次接触后感受到它们的强大,尤其是在异步调用方面的运用. 2 | 3 | RJIterator是该功能的OC实现,可以在OC/Swift项目中使用. 4 | 5 | #### 1. 异步的运用 6 | 7 | ##### 异步任务 8 | 在RJIterator中,一个RJAsyncClosure类型的闭包就是一个异步任务,可以被异步调度 9 | 10 | RJAsyncClosure 是RJIterator定义的闭包类型: 11 | 12 | ```Objective-C 13 | typedef void (^RJAsyncCallback)(id _Nullable value, id _Nullable error); 14 | typedef void (^RJAsyncClosure)(RJAsyncCallback _Nonnull callback); 15 | ``` 16 | 17 | 同时,RJIterator兼容PromiseKit,RJIterator会在运行时判断,如果一个对象是AnyPromise类型,也是异步任务. 18 | 19 | 20 | #### 异步块 21 | 使用rj_async声明一个异步块,块内是同步风格编写的代码,但将以异步方式调度执行. 22 | 23 | Objective-C: 24 | ```Objective-C 25 | rj_async(^{ 26 |    //同步风格的代码 27 | }) 28 | .finally(^{ 29 |    //收尾,总会走这里 30 | }); 31 | ``` 32 | 33 | Swift: 34 | ```Swift 35 | rj_async { 36 | //... 37 | } 38 | .finally { 39 | //... 40 | } 41 | ``` 42 | 43 | #### 以登录举例 44 | 45 | 比如有这样的登录场景: 登录成功 --> 查询个人信息 --> 下载头像 --> 给头像加特效 --> 进入详情. 46 | 47 | ##### 为了举例,假设要求每一步必须在上一步完成之后进行. 48 | 该功能可以使用异步块如下实现: 49 | 50 | ##### (1) 定义异步任务 51 | 52 | ```Objective-C 53 | //登录 54 | - (RJAsyncClosure)loginWithAccount:(NSString *)account pwd:(NSString *)pwd { 55 |   //返回RJAsyncClosurele类型block 56 |    return ^(RJAsyncCallback callback){ 57 |       //调用http接口 58 | post(@"/login", account, pwd, ^(id response, error) { 59 | callback(response.data, error); 60 | }); 61 | }; 62 | } 63 | //拉取信息 64 | - (RJAsyncClosure)queryInfoWithUid:(NSString *)uid token:(NSString *)token{ 65 | return ^(RJAsyncCallback callback){ 66 | get(@"query", uid, token, ^(id response, error) { 67 | callback(response.data, error); 68 | }); 69 | }; 70 | } 71 | //下载头像 72 | - (RJAsyncClosure)downloadHeadImage:(NSString *)url{ 73 | return ^(RJAsyncCallback callback){ 74 | get(@"file", url, ^(id response, error) { 75 | callback(response.data, error); 76 | }); 77 | }; 78 | } 79 | //处理头像 80 | - (RJAsyncClosure)makeEffect:(UIImage *)image{ 81 | return ^(RJAsyncCallback callback){ 82 | make(image, ^(id data, error) { 83 | callback(data, error); 84 | }); 85 | }; 86 | } 87 | ``` 88 | 89 | ##### (2)以同步风格编写代码 90 | ```Objective-C 91 | - (void)onLogin:(id)sender { 92 | rj_async(^{ 93 | //每次await 的 result 94 | RJResult *result = nil; 95 | 96 | [ProgressHud show]; 97 | 98 | //开始登录... 99 | result = rj_await( [self loginWithAccount:@"112233" pwd:@"12345"] ); 100 | if (result.error) { 101 | //登录失败 102 | return ; 103 | } 104 | //登录完成 105 | NSDictionary *login_josn = result.value; 106 | 107 | //开始拉取个人信息... 108 | result = rj_await( [self queryInfoWithUid:login_josn[@"uid"] token:login_josn[@"token"]] ); 109 | if (result.error) { 110 | //拉取个人信息失败 111 | return ; 112 | } 113 | //拉取个人信息完成 114 | NSDictionary *info_josn = result.value; 115 | 116 | //开始下载头像... 117 | result = rj_await( [self downloadHeadImage:info_josn[@"url"]] ); 118 | if (result.error) { 119 | //下载头像失败 120 | return ; 121 | } 122 | //下载头像完成 123 | UIImage *head_image = result.value; 124 | 125 | //开始处理头像 126 | result = rj_await( [self makeEffect:head_image] ); 127 | if (result.error) { 128 | //处理头像失败 129 | return ; 130 | } 131 | //处理头像完成 132 | head_image = result.value; 133 | 134 | //全部完成,进入详情界面 135 | UserInfoViewController *vc = [[UserInfoViewController alloc] init]; 136 | vc.uid = login_josn[@"uid"]; 137 | vc.token = login_josn[@"token"]; 138 | vc.name = info_josn[@"name"]; 139 | vc.headimg = head_image; 140 | [self presentViewController:vc animated:YES completion:NULL]; 141 | }) 142 | .finally(^{ 143 | //收尾 144 | [ProgressHud dismiss]; 145 | }); 146 | } 147 | ``` 148 | 149 | rj_async块内部完全以同步方式编写,通过把异步任务包装进rj_await(),rj_async会自动以异步方式调度它们,不会阻塞主流程,在主观感受上,它们是同步代码,功能逻辑也比较清晰.实际上,任何"等待异步回调 -> 下一步"类型的逻辑都可以转化成如上的写法. 150 | 151 | ##### rj_async块内部运行在主线程,可以直接在块内部进行UI操作. 152 | ##### rj_await可以理解为:"等待异步任务完成并返回结果",但是这种等待是不阻塞主线程的. 153 | 154 | 155 | RJIterator兼容PromiseKit.如果已有自己的一个Promise,可以在异步块内直接传给rj_await(),它会被正确异步调度, 但是只支持AnyPromise,如果不是AnyPromise,如果可以转化的话,使用PromiseKit提供的相关方法转为AnyPromise再使用. 156 | 比如: 157 | ```Swift 158 | rj_async { 159 | let fetchImage = URLSession.shared.dataTask(.promise, with: URL.init(string: "http://oem96wx6v.bkt.clouddn.com/bizhi-1030-1097-2xx.jpg")!).compactMap{ UIImage(data: $0.data) } 160 | let result = rj_await( AnyPromise.init(fetchImage) ) 161 | if let error = result.error { 162 | print("下载头像失败:\(error)") 163 | return 164 | } 165 | let image = result.value as! UIImage 166 | print("下载头像成功, image:\(image)") 167 | } 168 | ``` 169 | 170 | 下面是该登录功能举例对应的Swift写法: 171 | ```Swift 172 | //登录 173 | func login(account: String, pwd: String) -> RJAsyncClosure { 174 | //返回RJAsyncClosure类型闭包 175 | return { (callback: @escaping RJAsyncCallback) in 176 | //以asyncAfter 模拟Http请求 + 回调 177 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 178 | //登录成功 179 | callback(["uid": "80022", "token":"67625235555"], nil); 180 | }) 181 | }; 182 | } 183 | //查询个人信息 184 | func query(uid:String, token: String) -> RJAsyncClosure { 185 | return { (callback: @escaping RJAsyncCallback) in 186 | //以asyncAfter 模拟Http请求 + 回调 187 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 188 | //查询成功 189 | callback(["name": "JimGreen", "url":"http://oem96wx6v.bkt.clouddn.com/bizhi-1030-1097-2.jpg"], NSError.init(domain: "s2", code: -1, userInfo: nil)); 190 | }) 191 | }; 192 | } 193 | //下载头像 194 | func download(url: String) -> RJAsyncClosure { 195 | return {(callback: @escaping RJAsyncCallback) in 196 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 197 | do { 198 | let data: Data? = try Data.init(contentsOf: URL.init(string: url)!) 199 | let iamge = UIImage.init(data: data!) 200 | //下载成功 201 | callback(iamge, nil) 202 | } catch let error { 203 | //下载失败 204 | callback(nil, error) 205 | } 206 | }) 207 | }; 208 | } 209 | //处理头像 210 | func makeEffect(image: UIImage) -> RJAsyncClosure { 211 | return { (callback: @escaping RJAsyncCallback) in 212 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 213 | //处理成功 214 | callback(image, nil); 215 | }) 216 | }; 217 | } 218 | 219 | @objc func onLogin(_ sender: Any? = nil) { 220 | rj_async { 221 | //每次await 的 result 222 | var result: RJResult 223 | 224 |            ProgressHud.show() 225 |             226 |            //开始登录 227 | result = rj_await( self.login(account: "112233", pwd: "445566") ) 228 | if let error = result.error { 229 | //登录失败 230 | return 231 | } 232 | //登录成功 233 | let login_json = result.value as! [String: String] 234 | 235 | //开始查询信息 236 | result = rj_await( self.query(uid: login_json["uid"]!, token: login_json["token"]!) ) 237 | if let error = result.error { 238 | //查询信息失败 239 | return 240 | } 241 | //查询信息成功 242 | let info_json = result.value as! [String: String] 243 | 244 | //开始下载头像 245 | result = rj_await( self.download(url: info_json["url"]!) ) 246 | if let error = result.error { 247 | //下载头像失败 248 | return 249 | } 250 | //下载头像成功 251 | let image = result.value as! UIImage 252 | 253 | //开始处理头像 254 | result = rj_await( self.makeEffect(image: image) ) 255 | if let error = result.error { 256 | //处理头像失败 257 | return 258 | } 259 | //处理头像成功 260 | let beautiful_image = result.value as! UIImage 261 | 262 | //进入详情界面 263 | } 264 | .finally { 265 | //登录收尾 266 | ProgressHud.dismiss() 267 | } 268 | } 269 | ``` 270 | 271 | ##### 对比普通回调方式编写代码 272 | 如果以普通回调方式,则不论如何逃不出如下模式: 273 | 274 | ```Objective-C 275 | - (void)loginWithAccount:(NSString *)account pwd:(NSString *)pwd callback:(void (^)(id value, id error))callback { 276 | post(@"/login", account, pwd, ^(id response, error) { 277 | callback(response.data, error); 278 | }); 279 | } 280 | - (void)queryInfoWithUid:(NSString *)uid token:(NSString *)token callback:(void (^)(id value, id error))callback{ 281 | get(@"query", uid, token, ^(id response, error) { 282 | callback(response.data, error); 283 | }); 284 | } 285 | - (void)downloadHeadImage:(NSString *)url callback:(void (^)(id value, id error))callback{ 286 | get(@"file", url, ^(id response, error) { 287 | callback(response.data, error); 288 | }); 289 | } 290 | - (void)makeEffect:(UIImage *)image callback:(void (^)(id value, id error))callback{ 291 | make(image, ^(id data, error) { 292 | callback(data, error); 293 | }); 294 | } 295 | 296 | - (void)onLogin:(id)sender { 297 | [ProgressHud show]; 298 | [self loginWithAccount:@"112233" pwd:@"112345" callback:^(id value, id error) { 299 | if (error) { 300 | [ProgressHud dismiss]; 301 | NSLog(@"Error happened:%@", error); 302 | } 303 | else { 304 | NSDictionary *json = (NSDictionary *)value; 305 | [self queryInfoWithUid:json[@"uid"] token:json[@"token"] callback:^(id value, id error) { 306 | if (error) { 307 | [ProgressHud dismiss]; 308 | NSLog(@"Error happened:%@", error); 309 | } 310 | else { 311 | NSDictionary *json = (NSDictionary *)value; 312 | [self downloadHeadImage:json[@"url"] callback:^(id value, id error) { 313 | if (error) { 314 | [ProgressHud dismiss]; 315 | NSLog(@"Error happened:%@", error); 316 | } 317 | else { 318 | UIImage *image = (UIImage *)value; 319 | [self makeEffect:image callback:^(id value, id error) { 320 | if (error) { 321 | [ProgressHud dismiss]; 322 | NSLog(@"Error happened:%@", error); 323 | } 324 | else { 325 | [ProgressHud dismiss]; 326 | UIImage *image = (UIImage *)value; 327 | /* 328 | All done 329 | */ 330 | } 331 | }]; 332 | } 333 | }]; 334 | } 335 | }]; 336 | } 337 | }]; 338 | } 339 | ``` 340 | 这时 onLogin方法就掉进了传说中的回调地狱 341 | 342 | ##### 对比Promise链实现 343 | ```Objective-C 344 | //登录 345 | - (AnyPromise *)loginWithAccount:(NSString *)account pwd:(NSString *)pwd { 346 | //返回AnyPromise 347 | return [AnyPromise promiseWithAdapterBlock:^(PMKAdapter _Nonnull adapter) { 348 | //以dispatch_after模拟Http请求 349 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 350 | //登录成功 351 | adapter(@{@"uid": @"0001", @"token": @"ffccdd566"}, nil); 352 | }); 353 | }]; 354 | } 355 | 356 | //查询信息 357 | - (AnyPromise *)queryInfoWithUid:(NSString *)uid token:(NSString *)token{ 358 | return [AnyPromise promiseWithAdapterBlock:^(PMKAdapter _Nonnull adapter) { 359 | //以dispatch_after模拟Http请求 360 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 361 | //查询成功 362 | adapter(@{@"url": @"http://oem96wx6v.bkt.clouddn.com/bizhi-1030-1097-2.jpg", @"name": @"LiLei"}, 363 | /*[NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Query error, please check network"}]*/nil 364 | ); 365 | }); 366 | }]; 367 | } 368 | 369 | //下载头像 370 | - (AnyPromise *)downloadHeadImage:(NSString *)url{ 371 | return [AnyPromise promiseWithAdapterBlock:^(PMKAdapter _Nonnull adapter) { 372 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 373 | //下载头像 374 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 375 | adapter(data ? [UIImage imageWithData:data] : nil, 376 | data ? nil : [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Download error, please check network"}]); 377 | }); 378 | }]; 379 | } 380 | 381 | //处理头像 382 | - (AnyPromise *)makeEffect:(UIImage *)image{ 383 | return [AnyPromise promiseWithAdapterBlock:^(PMKAdapter _Nonnull adapter) { 384 | //以dispatch_after模拟处理过程 385 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 386 | //处理成功 387 | adapter(image, nil); 388 | }); 389 | }]; 390 | } 391 | 392 | - (void)onLogin:(id)sender { 393 | //开始登录 394 | NSString *__block uid = @""; 395 | NSString *__block token = @""; 396 | NSString *__block name = @""; 397 | 398 | [self loginWithAccount:@"112233" pwd:@"445566"] 399 | .then(^(NSDictionary *json){ 400 | //登录成功 401 | uid = json[@"uid"]; 402 | token = json[@"token"]; 403 | //开始查询信息 404 | return [self queryInfoWithUid:json[@"uid"] token:json[@"token"]]; 405 | }) 406 | .then(^(NSDictionary *json){ 407 | //查询信息成功 408 | name = json[@"name"]; 409 | //开始下载头像 410 | return [self downloadHeadImage:json[@"url"]]; 411 | }) 412 | .then(^(UIImage *image){ 413 | //下载头像成功 414 | //开始处理头像 415 | return [self makeEffect:image]; 416 | }) 417 | .then(^(UIImage *image){ 418 | //处理头像成功 419 | //进入详情 420 | UserInfoViewController *vc = [[UserInfoViewController alloc] init]; 421 | vc.uid = uid; 422 | vc.token = token; 423 | vc.name = name; 424 | vc.headimg = image; 425 | [self presentViewController:vc animated:YES completion:NULL]; 426 | }) 427 | .catch(^(id error) { 428 | //出错 429 | }) 430 | .ensure(^{ 431 | //收尾 432 | }); 433 | } 434 | ``` 435 | 436 | 1.catch到error后其实根本不知道具体是哪一步出错了,除非对每个error做明显标识,如果对每个error处理方式不同的话,catch块内难免if else: 437 | ```Objective-C 438 | if (error.code == LOGIN_ERROR) { 439 |    //登录出错处理 440 | } 441 | else if (error.code == QUERY_ERROR) { 442 |    //查询信息出错处理 443 | } 444 | else if (error.code == DOWNLOAD_ERROR) { 445 |    //下载出错处理 446 | } 447 | else if (error.code == MAKE_EFFECT_ERROR) { 448 |    //处理头像出错处理 449 | } 450 | ``` 451 | 452 | 2.每个then块内的变量作用域是局部的,假设要在后面某个then块B内访问A块内得到的数据,除非把A块内的局部变量提成全局的,就像`uid,token,name`,因为在最后一个then块需要用来赋值给详情界面控制器,或者将它们作为参数层层传递到B块,都不够方便. 453 | 454 | 3.程序逻辑分散在过多的代码块. 455 | 456 | #### 2.生成器与迭代器 457 | 458 | 生成器与迭代器的概念及用法. 可以参考ES6教程 459 | 460 | http://www.infoq.com/cn/articles/es6-in-depth-generators 461 | 462 | http://es6.ruanyifeng.com/#docs/generator 463 | 464 | ##### 在RJIterator中,满足以下条件的C/Objective-C/Swift方法,闭包即可以作为生成器: 465 | 466 | (1)返回值为id或void,接受最多8个id参数的OC类方法,实例方法,block;c函数;Swift类方法,实例方法. 467 | 468 | 469 | (2)返回值为id,接受一个参数的Swift函数,闭包. 470 | 471 | 生成器不能直接调用,需要通过RJIterator类的初始化方法创建迭代器,再通过迭代器访问生成器: 472 | 473 | ```Objective-C 474 | - (id _Nonnull)initWithFunc:(RJGenetarorFunc _Nonnull)func arg:(id _Nullable)arg; 475 | - (id _Nonnull)initWithTarget:(id _Nonnull)target selector:(SEL _Nonnull)selector, ...; 476 | - (id _Nonnull)initWithBlock:(id _Nonnull)block, ...; 477 | - (id _Nonnull)initWithTarget:(id _Nonnull)target selector:(SEL _Nonnull)selector args:(NSArray *_Nullable)args; 478 | - (id _Nonnull)initWithBlock:(id _Nullable (^ _Nonnull)(id _Nullable))block arg:(id _Nullable)arg; 479 | - (id _Nonnull)initWithStandardBlock:(dispatch_block_t _Nonnull)block; 480 | ``` 481 | 482 | 483 | ##### 低配版聊天机器人 484 | 假设talk是个会说话的机器人,按一下它回一句.则可以如下实现talk 485 | 486 | ```Swift 487 | func talk(arg: Any?) -> Any? { 488 | rj_yield("Hello, How are you?"); 489 | rj_yield("Today is Friday"); 490 | rj_yield("So yestday is Thursday"); 491 | rj_yield("And tomorrow is Saturday"); 492 | rj_yield("Over"); 493 | return "==talk done=="; 494 | } 495 | ``` 496 | 497 | 这时候talk就是一个生成器,每次调用都会返回“下一句话”.他会记住上次说到哪了. 498 | 调用方式必须是通过迭代器,所以下面先创建talk的迭代器,然后通过next方法依次获得应答. 499 | 500 | ```Swift 501 | var it: RJIterator; 502 | var r: RJResult; 503 | 504 | it = RJIterator.init(withFunc: talk, arg: nil) 505 | r = it.next() 506 | print("value: \(r.value), done:\(r.done)") 507 | //==> value: Hello How are you?, done:NO 508 | 509 | r = it.next() 510 | print("value: \(r.value), done:\(r.done)") 511 | //==> value: Today is Friday, done:NO 512 | 513 | r = it.next() 514 | print("value: \(r.value), done:\(r.done)") 515 | //==> value: So yestday is Thursday, done:NO 516 | 517 | r = it.next() 518 | print("value: \(r.value), done:\(r.done)") 519 | //==> value: And tomorrow is Saturday, done:NO 520 | 521 | r = it.next() 522 | print("value: \(r.value), done:\(r.done)") 523 | //==> value: Over, done:NO 524 | 525 | r = it.next() 526 | print("value: \(r.value), done:\(r.done)") 527 | //==> value: ==talk done==, done:YES 528 | 529 | r = it.next() 530 | print("value: \(r.value), done:\(r.done)") 531 | //==> value: ==talk done==, done:YES 532 | ``` 533 | RJResult是迭代器RJIterator每次next返回的结果值类型, 其中value表示结果数据, done表示是否迭代结束,结束表示生成器内部已经执行了尾部或者某处的return. 534 | 535 | 每次next调用,talk都会从rj_yield处返回,可以看作是暂时中断talk,等到再次执行next,talk又从上次中断的地方恢复继续执行,这种“切换”方式类似协程,只是RJIterator并不是一个完整的协程库,协程库大部分目的在于提高服务端性能,因此高效的协程调度很重要,而RJIterator核心在于实现yield原语和async块。 536 | 537 | 538 | ##### 新的需求 539 | 感觉还不够好,比如想要告诉机器人我的名字,以增进彼此感情. 540 | 修改talk: 541 | ```Swift 542 | func talk(name: Any?) -> Any? { 543 | rj_yield("Hello \(name), How are you?"); 544 | rj_yield("Today is Friday"); 545 | rj_yield("So yestday is Thursday"); 546 | rj_yield("And tomorrow is Saturday"); 547 | rj_yield("Over"); 548 | return "==talk done=="; 549 | } 550 | ``` 551 | 552 | 并在创建迭代器的时候给它传参: 553 | ```Swift 554 | it = RJIterator.init(withFunc: talk, arg: "乌卡卡") 555 | ``` 556 | 557 | 这时候第一次调用next,将如下返回: 558 | ``` 559 | value: Hello 乌卡卡, How are you?, done:NO 560 | ``` 561 | 562 | ##### 更新的需求 563 | 在第5次调用next和talk对话的时候,它回答了"Over",并且再次迭代(第6次)它就会结束, 但如果还想再聊几轮,可以在第6次迭代的时候,给他传命令,告诉机器人再来一发。 564 | 565 | 修改talk: 566 | ```Swift 567 | fileprivate func talk(name: Any?) -> Any? { 568 | var cmd = "" 569 | repeat { 570 | rj_yield("Hello \(name ?? ""), How are you?"); 571 | rj_yield("Today is Friday"); 572 | rj_yield("So yestday is Thursday"); 573 | rj_yield("And tomorrow is Saturday"); 574 | cmd = rj_yield("Over") as? String ?? ""; 575 | }while cmd != "Over" 576 | 577 | return "==talk done=="; 578 | } 579 | ``` 580 | 第6次调用next时传值 581 | ```Swift 582 | r = it.next("again") 583 | print("value: \(r.value), done:\(r.done)") 584 | //value: value: Hello 乌卡卡, How are you?, done:NO 585 | ``` 586 | 587 | 如果不传这个值,talk将按正常流程结束,但是传入"again"后它又从头开始了。 也就是生成器talk除了具备“中断+返回”的能力,还具备中间多次“传值进去”的能力,其中原理是: 588 | ###### 给next传的值将作为生成器内部上次rj_yield的新返回值,并在生成器“苏醒”的时候赋值给左边"cmd",如果next不传参,则该返回值就是rj_yield()本来包装的那个值. 通过这个特性,可以基于生成器与迭代器变种出新功能,rj_async块就是基于这个原理. 589 | 590 | ##### 生成器嵌套 591 | 生成器内部可以再嵌套调用别的生成器,比如要从1数到9,感觉工作量太大,所以定义3个生成器,每个负责数3个数, 再定义一个总的生成器,内部调用这三个小生成器 592 | ```Swift 593 | func count_1_3(_: Any?) -> Any? { 594 | rj_yield(1) 595 | rj_yield(2) 596 | return 3 597 | } 598 | func count_4_5(_: Any?) -> Any? { 599 | rj_yield(4) 600 | rj_yield(5) 601 | return 6 602 | } 603 | func count_7_9(_: Any?) -> Any? { 604 | rj_yield(7) 605 | rj_yield(8) 606 | return 9 607 | } 608 | 609 | func count(_: Any?) -> Any? { 610 | rj_yield(RJIterator.init(withFunc: count_1_3, arg: nil)) 611 | rj_yield(RJIterator.init(withFunc: count_4_5, arg: nil)) 612 | rj_yield(RJIterator.init(withFunc: count_7_9, arg: nil)) 613 | return nil 614 | } 615 | ``` 616 | 为count创建迭代器,连续执行next将得到value: 1, 2, 3, 4, 5, 6, 7, 8, 9 617 | 618 | 619 | #### 可能 620 | 苹果文档透露Swift以后的版本可能会新增异步,多任务方面的新特性,所以以后的Swift有可能也会像JS一样支持async,yield,await等功能. 621 | 622 | ## 安装 623 | pod 624 | 625 | ``` 626 | use_frameworks! 627 | 628 | pod 'RJIterator', '~> 1.1.5' 629 | 630 | #import 631 | 632 | import PromiseKit 633 | ``` 634 | 635 | 手动: 636 | 637 | RJIterator基于MRC管理内存,混有一个Swift文件, 所以手动添加进去还要改配置,加Bridge Header, 比较麻烦,建议pod 638 | 639 | 640 | 641 | 642 |   643 | 644 | 645 | 646 | 647 | 648 | =========补充线 20180427======= 649 | 650 | #### 为何rj_async,rj_await不支持catch 651 | 假如支持catch,出错后统一跳转到catch块: 652 | ```Objective-C 653 | rj_async {^ 654 |   //await直接等到value,而不是RJResult对象,包含error和value 655 |   id value = rj_await(异步操作); 656 | id value = rj_await(异步操作); 657 | id value = rj_await(异步操作); 658 | } 659 | .catch(^(id error) { 660 | //出错处理 661 | }) 662 | .finally(^{ 663 | //收尾 664 | }); 665 | ``` 666 | 支持统一catch错误,确实比起目前的版本用起来更加直观方便,几乎100%贴近JS下async/await的用法了. 667 | 668 | 其实,最初版就是这样实现的,但是经过推敲后发现一个无法解决的问题: `catch`引发内存泄露. 669 | 670 | 假如在上面的代码中,在异步块开始处分配了一个对象,那么一旦接下来的某个await过程触发异常了,将造成对象无法释放的问题: 671 | 672 | ![](http://oem96wx6v.bkt.clouddn.com/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-04-25%20%E4%B8%8B%E5%8D%884.49.40.png) 673 | 674 | 如上图所示,异步块首先分配了一个对象object,然后进行三个await操作,假设在第二个await的过程中,发生错误,那么程序执行流直接略过后面的所有代码,进入catch块处理错误。 675 | 676 | 实际上这时不管在MRC,还是ARC下,object都无法得到释放。在ARC下,大部分情况下编译器为object生成的释放代码都处于代码块末端: 677 | 678 | ![](http://oem96wx6v.bkt.clouddn.com/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-04-25%20%E4%B8%8B%E5%8D%885.15.50.png) 679 | 680 | 如果执行流从中间断开了,那么这些释放代码永远执行不到,将造成“每逢出错必泄露”。而这种第三方实现的异常方式有别OC原生方式,无法获得编译器对”异常后内存泄露“问题的修复支持。 681 | 682 | 因此,在使用rj_async/rj_await进行异步编程的过程中,每一步await后要怎么处理错误,由每一步自己决定,不再统一跳转到catch块.且事实上实际开发场景中,每一步的出错处理方式是不同的,至少提示方式都不同,因此统一跳转到catch块处理错误的方式在大部分情况下反而造成不便。 683 | 684 | -------------------------------------------------------------------------------- /RJIterator.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint RJIterator.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "RJIterator" 19 | s.version = "1.1.5" 20 | s.summary = "生成器与迭代器的OC实现" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = <<-DESC 28 | 生成器与迭代器的OC实现,实现类似ES6的yied语意,实现async异步块,支持在iOS项目中以同步风格编写异步代码,避免了回调链和Promise链. 29 | DESC 30 | 31 | s.homepage = "https://github.com/renjinkui2719/RJIterator.git" 32 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 33 | 34 | 35 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # Licensing your code is important. See http://choosealicense.com for more info. 38 | # CocoaPods will detect a license file if there is a named LICENSE* 39 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 40 | # 41 | 42 | # s.license = "MIT (example)" 43 | s.license = { :type => "MIT", :file => "LICENSE" } 44 | 45 | 46 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 47 | # 48 | # Specify the authors of the library, with email addresses. Email addresses 49 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 50 | # accepts just a name if you'd rather not provide an email address. 51 | # 52 | # Specify a social_media_url where others can refer to, for example a twitter 53 | # profile URL. 54 | # 55 | 56 | s.author = { "renjinkui" => "2280278615@qq.com" } 57 | # Or just: s.author = "renjinkui" 58 | # s.authors = { "renjinkui" => "2280278615@qq.com" } 59 | # s.social_media_url = "http://twitter.com/renjinkui" 60 | 61 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 62 | # 63 | # If this Pod runs only on iOS or OS X, then specify the platform and 64 | # the deployment target. You can optionally include the target after the platform. 65 | # 66 | 67 | # s.platform = :ios 68 | s.platform = :ios, "8.0" 69 | 70 | # When using multiple platforms 71 | # s.ios.deployment_target = "5.0" 72 | # s.osx.deployment_target = "10.7" 73 | # s.watchos.deployment_target = "2.0" 74 | # s.tvos.deployment_target = "9.0" 75 | 76 | 77 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 78 | # 79 | # Specify the location from where the source should be retrieved. 80 | # Supports git, hg, bzr, svn and HTTP. 81 | # 82 | 83 | s.source = { :git => "https://github.com/renjinkui2719/RJIterator.git", :tag => "#{s.version}" } 84 | 85 | 86 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # CocoaPods is smart about how it includes source code. For source files 89 | # giving a folder will include any swift, h, m, mm, c & cpp files. 90 | # For header files it will include any header in the folder. 91 | # Not including the public_header_files will make all headers public. 92 | # 93 | 94 | s.source_files = "RJIterator/Class/*.{h,m,swift}" 95 | # s.exclude_files = "Classes/Exclude" 96 | 97 | s.public_header_files = "RJIterator/Class/*.h" 98 | 99 | 100 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 101 | # 102 | # A list of resources included with the Pod. These are copied into the 103 | # target bundle with a build phase script. Anything else will be cleaned. 104 | # You can preserve files from being cleaned, please don't preserve 105 | # non-essential files like tests, examples and documentation. 106 | # 107 | 108 | # s.resource = "icon.png" 109 | # s.resources = "Resources/*.png" 110 | 111 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 112 | 113 | 114 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 115 | # 116 | # Link your library with frameworks, or libraries. Libraries do not include 117 | # the lib prefix of their name. 118 | # 119 | 120 | # s.framework = "SomeFramework" 121 | # s.frameworks = "SomeFramework", "AnotherFramework" 122 | 123 | # s.library = "iconv" 124 | # s.libraries = "iconv", "xml2" 125 | 126 | 127 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 128 | # 129 | # If your library depends on compiler flags you can set them in the xcconfig hash 130 | # where they will only apply to your library. If you depend on other Podspecs 131 | # you can include multiple dependencies to ensure it works. 132 | 133 | s.requires_arc = false 134 | 135 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 136 | # s.dependency "JSONKit", "~> 1.4" 137 | 138 | s.swift_version = "3.2" 139 | 140 | end 141 | -------------------------------------------------------------------------------- /RJIterator.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 9148ABB82080EB820057DB14 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9148ABB72080EB820057DB14 /* AppDelegate.m */; }; 11 | 9148ABBB2080EB830057DB14 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9148ABBA2080EB830057DB14 /* ViewController.m */; }; 12 | 9148ABBE2080EB830057DB14 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9148ABBC2080EB830057DB14 /* Main.storyboard */; }; 13 | 9148ABC02080EB830057DB14 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9148ABBF2080EB830057DB14 /* Assets.xcassets */; }; 14 | 9148ABC32080EB830057DB14 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9148ABC12080EB830057DB14 /* LaunchScreen.storyboard */; }; 15 | 9148ABC62080EB830057DB14 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9148ABC52080EB830057DB14 /* main.m */; }; 16 | 9148ABCF2080EBEC0057DB14 /* RJIterator.m in Sources */ = {isa = PBXBuildFile; fileRef = 9148ABCE2080EBEC0057DB14 /* RJIterator.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; 17 | 91CB6A052081889F004180E0 /* TestsSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91CB6A042081889F004180E0 /* TestsSwift.swift */; }; 18 | 91CB6A08208188CB004180E0 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 91CB6A06208188CA004180E0 /* Tests.m */; }; 19 | 91CB6A0D208191F6004180E0 /* UserInfoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 91CB6A0C208191F6004180E0 /* UserInfoViewController.m */; }; 20 | 91CB6A0F2081AD1F004180E0 /* RJAsyncClosureCaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91CB6A0E2081AD1F004180E0 /* RJAsyncClosureCaller.swift */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 9148ABB32080EB820057DB14 /* RJIterator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RJIterator.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 9148ABB62080EB820057DB14 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 26 | 9148ABB72080EB820057DB14 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 27 | 9148ABB92080EB820057DB14 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 28 | 9148ABBA2080EB830057DB14 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 29 | 9148ABBD2080EB830057DB14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | 9148ABBF2080EB830057DB14 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 9148ABC22080EB830057DB14 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 9148ABC42080EB830057DB14 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 9148ABC52080EB830057DB14 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 34 | 9148ABCD2080EBEC0057DB14 /* RJIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RJIterator.h; sourceTree = ""; }; 35 | 9148ABCE2080EBEC0057DB14 /* RJIterator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RJIterator.m; sourceTree = ""; }; 36 | 91CB6A032081889E004180E0 /* RJIterator-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RJIterator-Bridging-Header.h"; sourceTree = ""; }; 37 | 91CB6A042081889F004180E0 /* TestsSwift.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestsSwift.swift; sourceTree = ""; }; 38 | 91CB6A06208188CA004180E0 /* Tests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = ""; }; 39 | 91CB6A07208188CA004180E0 /* Tests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tests.h; sourceTree = ""; }; 40 | 91CB6A0B208191F6004180E0 /* UserInfoViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserInfoViewController.h; sourceTree = ""; }; 41 | 91CB6A0C208191F6004180E0 /* UserInfoViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserInfoViewController.m; sourceTree = ""; }; 42 | 91CB6A0E2081AD1F004180E0 /* RJAsyncClosureCaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RJAsyncClosureCaller.swift; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | 9148ABB02080EB820057DB14 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 9148ABAA2080EB820057DB14 = { 57 | isa = PBXGroup; 58 | children = ( 59 | 9148ABB52080EB820057DB14 /* RJIterator */, 60 | 9148ABB42080EB820057DB14 /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | 9148ABB42080EB820057DB14 /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 9148ABB32080EB820057DB14 /* RJIterator.app */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | 9148ABB52080EB820057DB14 /* RJIterator */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 9148ABCC2080EBCC0057DB14 /* Class */, 76 | 9148ABB62080EB820057DB14 /* AppDelegate.h */, 77 | 9148ABB72080EB820057DB14 /* AppDelegate.m */, 78 | 9148ABB92080EB820057DB14 /* ViewController.h */, 79 | 9148ABBA2080EB830057DB14 /* ViewController.m */, 80 | 91CB6A0B208191F6004180E0 /* UserInfoViewController.h */, 81 | 91CB6A0C208191F6004180E0 /* UserInfoViewController.m */, 82 | 91CB6A07208188CA004180E0 /* Tests.h */, 83 | 91CB6A06208188CA004180E0 /* Tests.m */, 84 | 91CB6A042081889F004180E0 /* TestsSwift.swift */, 85 | 9148ABBC2080EB830057DB14 /* Main.storyboard */, 86 | 9148ABBF2080EB830057DB14 /* Assets.xcassets */, 87 | 9148ABC12080EB830057DB14 /* LaunchScreen.storyboard */, 88 | 9148ABC42080EB830057DB14 /* Info.plist */, 89 | 9148ABC52080EB830057DB14 /* main.m */, 90 | 91CB6A032081889E004180E0 /* RJIterator-Bridging-Header.h */, 91 | ); 92 | path = RJIterator; 93 | sourceTree = ""; 94 | }; 95 | 9148ABCC2080EBCC0057DB14 /* Class */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 9148ABCD2080EBEC0057DB14 /* RJIterator.h */, 99 | 9148ABCE2080EBEC0057DB14 /* RJIterator.m */, 100 | 91CB6A0E2081AD1F004180E0 /* RJAsyncClosureCaller.swift */, 101 | ); 102 | path = Class; 103 | sourceTree = ""; 104 | }; 105 | /* End PBXGroup section */ 106 | 107 | /* Begin PBXNativeTarget section */ 108 | 9148ABB22080EB820057DB14 /* RJIterator */ = { 109 | isa = PBXNativeTarget; 110 | buildConfigurationList = 9148ABC92080EB830057DB14 /* Build configuration list for PBXNativeTarget "RJIterator" */; 111 | buildPhases = ( 112 | 9148ABAF2080EB820057DB14 /* Sources */, 113 | 9148ABB02080EB820057DB14 /* Frameworks */, 114 | 9148ABB12080EB820057DB14 /* Resources */, 115 | ); 116 | buildRules = ( 117 | ); 118 | dependencies = ( 119 | ); 120 | name = RJIterator; 121 | productName = RJIterator; 122 | productReference = 9148ABB32080EB820057DB14 /* RJIterator.app */; 123 | productType = "com.apple.product-type.application"; 124 | }; 125 | /* End PBXNativeTarget section */ 126 | 127 | /* Begin PBXProject section */ 128 | 9148ABAB2080EB820057DB14 /* Project object */ = { 129 | isa = PBXProject; 130 | attributes = { 131 | LastUpgradeCheck = 0920; 132 | ORGANIZATIONNAME = renjinkui; 133 | TargetAttributes = { 134 | 9148ABB22080EB820057DB14 = { 135 | CreatedOnToolsVersion = 9.2; 136 | LastSwiftMigration = 0920; 137 | ProvisioningStyle = Automatic; 138 | }; 139 | }; 140 | }; 141 | buildConfigurationList = 9148ABAE2080EB820057DB14 /* Build configuration list for PBXProject "RJIterator" */; 142 | compatibilityVersion = "Xcode 8.0"; 143 | developmentRegion = en; 144 | hasScannedForEncodings = 0; 145 | knownRegions = ( 146 | en, 147 | Base, 148 | ); 149 | mainGroup = 9148ABAA2080EB820057DB14; 150 | productRefGroup = 9148ABB42080EB820057DB14 /* Products */; 151 | projectDirPath = ""; 152 | projectRoot = ""; 153 | targets = ( 154 | 9148ABB22080EB820057DB14 /* RJIterator */, 155 | ); 156 | }; 157 | /* End PBXProject section */ 158 | 159 | /* Begin PBXResourcesBuildPhase section */ 160 | 9148ABB12080EB820057DB14 /* Resources */ = { 161 | isa = PBXResourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 9148ABC32080EB830057DB14 /* LaunchScreen.storyboard in Resources */, 165 | 9148ABC02080EB830057DB14 /* Assets.xcassets in Resources */, 166 | 9148ABBE2080EB830057DB14 /* Main.storyboard in Resources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXResourcesBuildPhase section */ 171 | 172 | /* Begin PBXSourcesBuildPhase section */ 173 | 9148ABAF2080EB820057DB14 /* Sources */ = { 174 | isa = PBXSourcesBuildPhase; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | 9148ABBB2080EB830057DB14 /* ViewController.m in Sources */, 178 | 9148ABCF2080EBEC0057DB14 /* RJIterator.m in Sources */, 179 | 91CB6A052081889F004180E0 /* TestsSwift.swift in Sources */, 180 | 91CB6A08208188CB004180E0 /* Tests.m in Sources */, 181 | 9148ABC62080EB830057DB14 /* main.m in Sources */, 182 | 91CB6A0D208191F6004180E0 /* UserInfoViewController.m in Sources */, 183 | 9148ABB82080EB820057DB14 /* AppDelegate.m in Sources */, 184 | 91CB6A0F2081AD1F004180E0 /* RJAsyncClosureCaller.swift in Sources */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXSourcesBuildPhase section */ 189 | 190 | /* Begin PBXVariantGroup section */ 191 | 9148ABBC2080EB830057DB14 /* Main.storyboard */ = { 192 | isa = PBXVariantGroup; 193 | children = ( 194 | 9148ABBD2080EB830057DB14 /* Base */, 195 | ); 196 | name = Main.storyboard; 197 | sourceTree = ""; 198 | }; 199 | 9148ABC12080EB830057DB14 /* LaunchScreen.storyboard */ = { 200 | isa = PBXVariantGroup; 201 | children = ( 202 | 9148ABC22080EB830057DB14 /* Base */, 203 | ); 204 | name = LaunchScreen.storyboard; 205 | sourceTree = ""; 206 | }; 207 | /* End PBXVariantGroup section */ 208 | 209 | /* Begin XCBuildConfiguration section */ 210 | 9148ABC72080EB830057DB14 /* Debug */ = { 211 | isa = XCBuildConfiguration; 212 | buildSettings = { 213 | ALWAYS_SEARCH_USER_PATHS = NO; 214 | CLANG_ANALYZER_NONNULL = YES; 215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 217 | CLANG_CXX_LIBRARY = "libc++"; 218 | CLANG_ENABLE_MODULES = YES; 219 | CLANG_ENABLE_OBJC_ARC = YES; 220 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 221 | CLANG_WARN_BOOL_CONVERSION = YES; 222 | CLANG_WARN_COMMA = YES; 223 | CLANG_WARN_CONSTANT_CONVERSION = YES; 224 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 225 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 226 | CLANG_WARN_EMPTY_BODY = YES; 227 | CLANG_WARN_ENUM_CONVERSION = YES; 228 | CLANG_WARN_INFINITE_RECURSION = YES; 229 | CLANG_WARN_INT_CONVERSION = YES; 230 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 231 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 234 | CLANG_WARN_STRICT_PROTOTYPES = YES; 235 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 236 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 237 | CLANG_WARN_UNREACHABLE_CODE = YES; 238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 239 | CODE_SIGN_IDENTITY = "iPhone Developer"; 240 | COPY_PHASE_STRIP = NO; 241 | DEBUG_INFORMATION_FORMAT = dwarf; 242 | ENABLE_STRICT_OBJC_MSGSEND = YES; 243 | ENABLE_TESTABILITY = YES; 244 | GCC_C_LANGUAGE_STANDARD = gnu11; 245 | GCC_DYNAMIC_NO_PIC = NO; 246 | GCC_NO_COMMON_BLOCKS = YES; 247 | GCC_OPTIMIZATION_LEVEL = 0; 248 | GCC_PREPROCESSOR_DEFINITIONS = ( 249 | "DEBUG=1", 250 | "$(inherited)", 251 | ); 252 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 253 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 254 | GCC_WARN_UNDECLARED_SELECTOR = YES; 255 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 256 | GCC_WARN_UNUSED_FUNCTION = YES; 257 | GCC_WARN_UNUSED_VARIABLE = YES; 258 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 259 | MTL_ENABLE_DEBUG_INFO = YES; 260 | ONLY_ACTIVE_ARCH = YES; 261 | SDKROOT = iphoneos; 262 | }; 263 | name = Debug; 264 | }; 265 | 9148ABC82080EB830057DB14 /* Release */ = { 266 | isa = XCBuildConfiguration; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 271 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 272 | CLANG_CXX_LIBRARY = "libc++"; 273 | CLANG_ENABLE_MODULES = YES; 274 | CLANG_ENABLE_OBJC_ARC = YES; 275 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 276 | CLANG_WARN_BOOL_CONVERSION = YES; 277 | CLANG_WARN_COMMA = YES; 278 | CLANG_WARN_CONSTANT_CONVERSION = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 281 | CLANG_WARN_EMPTY_BODY = YES; 282 | CLANG_WARN_ENUM_CONVERSION = YES; 283 | CLANG_WARN_INFINITE_RECURSION = YES; 284 | CLANG_WARN_INT_CONVERSION = YES; 285 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 286 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 287 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 288 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 289 | CLANG_WARN_STRICT_PROTOTYPES = YES; 290 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 291 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 292 | CLANG_WARN_UNREACHABLE_CODE = YES; 293 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 294 | CODE_SIGN_IDENTITY = "iPhone Developer"; 295 | COPY_PHASE_STRIP = NO; 296 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 297 | ENABLE_NS_ASSERTIONS = NO; 298 | ENABLE_STRICT_OBJC_MSGSEND = YES; 299 | GCC_C_LANGUAGE_STANDARD = gnu11; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 302 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 303 | GCC_WARN_UNDECLARED_SELECTOR = YES; 304 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 305 | GCC_WARN_UNUSED_FUNCTION = YES; 306 | GCC_WARN_UNUSED_VARIABLE = YES; 307 | IPHONEOS_DEPLOYMENT_TARGET = 11.2; 308 | MTL_ENABLE_DEBUG_INFO = NO; 309 | SDKROOT = iphoneos; 310 | VALIDATE_PRODUCT = YES; 311 | }; 312 | name = Release; 313 | }; 314 | 9148ABCA2080EB830057DB14 /* Debug */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = YES; 318 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 319 | CLANG_ENABLE_MODULES = YES; 320 | CODE_SIGN_STYLE = Automatic; 321 | DEVELOPMENT_TEAM = B5VVHWEWBM; 322 | GCC_PREPROCESSOR_DEFINITIONS = "DEMO=1"; 323 | INFOPLIST_FILE = RJIterator/Info.plist; 324 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 325 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 326 | PRODUCT_BUNDLE_IDENTIFIER = com.rjk.RJIterator; 327 | PRODUCT_NAME = "$(TARGET_NAME)"; 328 | SWIFT_OBJC_BRIDGING_HEADER = "RJIterator/RJIterator-Bridging-Header.h"; 329 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 330 | SWIFT_VERSION = 3.0; 331 | TARGETED_DEVICE_FAMILY = "1,2"; 332 | }; 333 | name = Debug; 334 | }; 335 | 9148ABCB2080EB830057DB14 /* Release */ = { 336 | isa = XCBuildConfiguration; 337 | buildSettings = { 338 | ALWAYS_SEARCH_USER_PATHS = YES; 339 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 340 | CLANG_ENABLE_MODULES = YES; 341 | CODE_SIGN_STYLE = Automatic; 342 | DEVELOPMENT_TEAM = B5VVHWEWBM; 343 | GCC_PREPROCESSOR_DEFINITIONS = "DEMO=1"; 344 | INFOPLIST_FILE = RJIterator/Info.plist; 345 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 346 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 347 | PRODUCT_BUNDLE_IDENTIFIER = com.rjk.RJIterator; 348 | PRODUCT_NAME = "$(TARGET_NAME)"; 349 | SWIFT_OBJC_BRIDGING_HEADER = "RJIterator/RJIterator-Bridging-Header.h"; 350 | SWIFT_VERSION = 3.0; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Release; 354 | }; 355 | /* End XCBuildConfiguration section */ 356 | 357 | /* Begin XCConfigurationList section */ 358 | 9148ABAE2080EB820057DB14 /* Build configuration list for PBXProject "RJIterator" */ = { 359 | isa = XCConfigurationList; 360 | buildConfigurations = ( 361 | 9148ABC72080EB830057DB14 /* Debug */, 362 | 9148ABC82080EB830057DB14 /* Release */, 363 | ); 364 | defaultConfigurationIsVisible = 0; 365 | defaultConfigurationName = Release; 366 | }; 367 | 9148ABC92080EB830057DB14 /* Build configuration list for PBXNativeTarget "RJIterator" */ = { 368 | isa = XCConfigurationList; 369 | buildConfigurations = ( 370 | 9148ABCA2080EB830057DB14 /* Debug */, 371 | 9148ABCB2080EB830057DB14 /* Release */, 372 | ); 373 | defaultConfigurationIsVisible = 0; 374 | defaultConfigurationName = Release; 375 | }; 376 | /* End XCConfigurationList section */ 377 | }; 378 | rootObject = 9148ABAB2080EB820057DB14 /* Project object */; 379 | } 380 | -------------------------------------------------------------------------------- /RJIterator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RJIterator/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/13. 6 | // Copyright © 2018年 renjinkui. 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 | -------------------------------------------------------------------------------- /RJIterator/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/13. 6 | // Copyright © 2018年 renjinkui. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // 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. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /RJIterator/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 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /RJIterator/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 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /RJIterator/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 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /RJIterator/Class/RJAsyncClosureCaller.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RJAsyncClosureCaller.swift 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/14. 6 | // Copyright © 2018年 renjinkui. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class RJAsyncClosureCaller: NSObject { 12 | @objc public static func call(closure: Any, finish: @escaping RJAsyncCallback) -> Void { 13 | (closure as? RJAsyncClosure)?(finish) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /RJIterator/Class/RJIterator.h: -------------------------------------------------------------------------------- 1 | // 2 | // RJIterator.h 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/12. 6 | // Copyright © 2018年 JK. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class RJResult; 12 | @class RJIterator; 13 | @class RJAsyncEpilog; 14 | 15 | id _Nullable rj_yield(id _Nullable value); 16 | RJResult * _Nonnull rj_await(id _Nullable value); 17 | RJAsyncEpilog *_Nonnull rj_async(dispatch_block_t _Nonnull block); 18 | 19 | 20 | typedef id _Nullable (*RJGenetarorFunc)(id _Nullable); 21 | typedef void (^RJAsyncCallback)(id _Nullable value, id _Nullable error); 22 | typedef void (^RJAsyncClosure)(RJAsyncCallback _Nonnull callback); 23 | 24 | @interface RJIterator : NSObject 25 | { 26 | int *_ev_leave; 27 | int *_ev_entry; 28 | BOOL _ev_entry_valid; 29 | void *_stack; 30 | int _stack_size; 31 | RJIterator * _nest; 32 | RJGenetarorFunc _func; 33 | id _target; 34 | SEL _selector; 35 | id _block; 36 | NSMutableArray *_args; 37 | NSMethodSignature *_signature; 38 | BOOL _done; 39 | id _value; 40 | id _error; 41 | } 42 | 43 | 44 | - (id _Nonnull)initWithFunc:(RJGenetarorFunc _Nonnull)func arg:(id _Nullable)arg; 45 | - (id _Nonnull)initWithTarget:(id _Nonnull)target selector:(SEL _Nonnull)selector, ...; 46 | - (id _Nonnull)initWithBlock:(id _Nonnull)block, ...; 47 | 48 | //兼容swift 49 | - (id _Nonnull)initWithTarget:(id _Nonnull)target selector:(SEL _Nonnull)selector args:(NSArray *_Nullable)args; 50 | - (id _Nonnull)initWithBlock:(id _Nullable (^ _Nonnull)(id _Nullable))block arg:(id _Nullable)arg; 51 | - (id _Nonnull)initWithStandardBlock:(dispatch_block_t _Nonnull)block; 52 | 53 | 54 | - (RJResult * _Nonnull)next; 55 | - (RJResult * _Nonnull)next:(id _Nonnull)value; 56 | @end 57 | 58 | 59 | @interface RJResult: NSObject 60 | { 61 | id _value; 62 | BOOL _done; 63 | } 64 | @property (nonatomic, strong, readonly) id _Nullable value; 65 | @property (nonatomic, strong, readonly) id _Nullable error; 66 | @property (nonatomic, readonly) BOOL done; 67 | + (instancetype _Nonnull)resultWithValue:(id _Nullable)value error:(id _Nullable)error done:(BOOL)done; 68 | @end 69 | 70 | 71 | typedef void (^RJFinallyConfiger)(dispatch_block_t _Nonnull); 72 | @interface RJAsyncEpilog: NSObject 73 | { 74 | id _finally_handler; 75 | } 76 | @property (nonatomic, readonly) RJFinallyConfiger _Nonnull finally ; 77 | @end 78 | -------------------------------------------------------------------------------- /RJIterator/Class/RJIterator.m: -------------------------------------------------------------------------------- 1 | // 2 | // RJIterator.m 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/12. 6 | // Copyright © 2018年 JK. All rights reserved. 7 | // 8 | 9 | #import "RJIterator.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | #if DEMO 16 | #import "RJIterator-Swift.h" 17 | #else 18 | #import 19 | #endif 20 | 21 | #if __has_feature(objc_arc) 22 | //ARC下存在跳转导致的编译器生成的释放函数执行不到的问题 23 | //MRC就可以回避编译器的这种 “干扰” 24 | //以pod 安装可正常运行,不用手动配MRC 25 | #error RJIterator Must be compiled with MRC 26 | #endif 27 | 28 | #define DEFAULT_STACK_SIZE (256 * 1024) 29 | 30 | #define JMP_CONTINUE 1 31 | #define JMP_DONE 2 32 | 33 | #define is_null(arg) (!(arg) || [(arg) isKindOfClass:NSNull.self]) 34 | #define arg_or_nil(arg) (is_null(arg) ? nil : arg) 35 | 36 | static NSMethodSignature *NSMethodSignatureForBlock(id block); 37 | 38 | #pragma mark - RJAsyncEpilog 39 | @interface RJAsyncEpilog() 40 | - (void)do_finally; 41 | @property (nonatomic, copy) dispatch_block_t finally_hanler; 42 | @end 43 | 44 | @implementation RJAsyncEpilog 45 | @synthesize finally_hanler = _finally_handler; 46 | 47 | - (RJFinallyConfiger)finally { 48 | RJFinallyConfiger configer = ^(dispatch_block_t handler){ 49 | self.finally_hanler = handler; 50 | }; 51 | return [[(id)configer copy] autorelease]; 52 | } 53 | 54 | - (void)dealloc { 55 | #if DEMO 56 | NSLog(@"== %@ dealoc", self); 57 | #endif 58 | if (_finally_handler) { 59 | Block_release(_finally_handler); 60 | } 61 | [super dealloc]; 62 | } 63 | 64 | - (void)do_finally { 65 | if (_finally_handler) { 66 | ((dispatch_block_t)_finally_handler)(); 67 | } 68 | } 69 | 70 | @end 71 | 72 | #pragma mark - RJResult 73 | 74 | @implementation RJResult 75 | @synthesize value = _value; 76 | @synthesize done = _done; 77 | 78 | - (id)initWithValue:(id)value error:(id)error done:(BOOL)done { 79 | if (self = [super init]) { 80 | _value = [value retain]; 81 | _error = [error retain]; 82 | _done = done; 83 | } 84 | return self; 85 | } 86 | 87 | - (void)dealloc { 88 | #if DEMO 89 | NSLog(@"== %@ dealoc", self); 90 | #endif 91 | [_value release]; 92 | [_error release]; 93 | [super dealloc]; 94 | } 95 | 96 | + (instancetype)resultWithValue:(id)value error:(id)error done:(BOOL)done{ 97 | return [[[self alloc] initWithValue:value error:error done:done] autorelease]; 98 | } 99 | @end 100 | 101 | #pragma mark - RJIteratorStack 102 | 103 | static pthread_key_t iterator_stack_key; 104 | static void destroy_iterator_stack(void * stack) { 105 | CFRelease((CFArrayRef)stack); 106 | } 107 | 108 | @interface RJIteratorStack: NSObject 109 | + (void)push:(RJIterator *)iterator; 110 | + (RJIterator *)pop; 111 | + (RJIterator *)top; 112 | @end 113 | 114 | @implementation RJIteratorStack 115 | + (void)load { 116 | pthread_key_create(&iterator_stack_key, destroy_iterator_stack); 117 | } 118 | 119 | + (void)push:(RJIterator *)iterator { 120 | CFMutableArrayRef stack = pthread_getspecific(iterator_stack_key); 121 | if (!stack) { 122 | stack = CFArrayCreateMutable(kCFAllocatorSystemDefault, 16, &kCFTypeArrayCallBacks); 123 | pthread_setspecific(iterator_stack_key, (void *)stack); 124 | } 125 | CFArrayAppendValue(stack, (void *)iterator); 126 | } 127 | 128 | + (RJIterator *)pop { 129 | CFMutableArrayRef stack = pthread_getspecific(iterator_stack_key); 130 | CFIndex count = stack ? CFArrayGetCount(stack) : 0; 131 | if (count > 0) { 132 | RJIterator *iterator = (RJIterator *)CFArrayGetValueAtIndex(stack, count - 1); 133 | [iterator retain]; 134 | CFArrayRemoveValueAtIndex(stack, count - 1); 135 | return iterator.autorelease; 136 | } 137 | return nil; 138 | } 139 | 140 | + (RJIterator *)top { 141 | CFMutableArrayRef stack = pthread_getspecific(iterator_stack_key); 142 | CFIndex count = stack ? CFArrayGetCount(stack) : 0; 143 | if (count > 0) { 144 | RJIterator *iterator = (RJIterator *)CFArrayGetValueAtIndex(stack, count - 1); 145 | return iterator; 146 | } 147 | return nil; 148 | } 149 | @end 150 | 151 | #pragma mark - RJIterator 152 | 153 | @interface RJIterator() 154 | @property (nonatomic, strong) RJIterator * nest; 155 | @property (nonatomic, strong) id value; 156 | @property (nonatomic, strong) id error; 157 | @property (nonatomic, assign) BOOL done; 158 | @end 159 | 160 | @implementation RJIterator 161 | @synthesize nest = _nest; 162 | @synthesize value = _value; 163 | @synthesize error = _error; 164 | @synthesize done = _done; 165 | 166 | - (id)init { 167 | if (self = [super init]) { 168 | _stack = malloc(DEFAULT_STACK_SIZE); 169 | memset(_stack, 0x00, DEFAULT_STACK_SIZE); 170 | _stack_size = DEFAULT_STACK_SIZE; 171 | 172 | _ev_leave = malloc(sizeof(jmp_buf)); 173 | memset(_ev_leave, 0x00, sizeof(jmp_buf)); 174 | _ev_entry = malloc(sizeof(jmp_buf)); 175 | memset(_ev_entry, 0x00, sizeof(jmp_buf)); 176 | 177 | _args = [NSMutableArray arrayWithCapacity:8].retain; 178 | } 179 | return self; 180 | } 181 | 182 | - (void)dealloc { 183 | #if DEMO 184 | NSLog(@"== %@ dealoc", self); 185 | #endif 186 | [_args release]; 187 | [_target release]; 188 | [_signature release]; 189 | [_value release]; 190 | [_error release]; 191 | [_nest release]; 192 | 193 | if (_stack) { 194 | free(_stack); 195 | _stack = NULL; 196 | } 197 | if (_ev_leave) { 198 | free(_ev_leave); 199 | _ev_leave = NULL; 200 | } 201 | if (_ev_entry) { 202 | free(_ev_entry); 203 | _ev_entry = NULL; 204 | } 205 | 206 | [super dealloc]; 207 | } 208 | 209 | - (id _Nonnull)initWithFunc:(RJGenetarorFunc _Nonnull)func arg:(id _Nullable)arg { 210 | if (self = [self init]) { 211 | _func = func; 212 | [_args addObject:arg ?: NSNull.null]; 213 | } 214 | return self; 215 | } 216 | 217 | - (id _Nonnull)initWithTarget:(id _Nonnull)target selector:(SEL _Nonnull)selector, ... { 218 | NSAssert(target && selector, @"target and selector must not be nil"); 219 | 220 | NSMethodSignature *signature = [self.class signatureForTarget:target selector:selector]; 221 | [self.class checkGeneratorSignature:signature is_block:NO]; 222 | 223 | NSMutableArray *args = [NSMutableArray array]; 224 | va_list ap; 225 | va_start(ap, selector); 226 | for (int i=0; i < (int)signature.numberOfArguments - 2; ++i) { 227 | id arg = va_arg(ap, id); 228 | [args addObject:arg ?: NSNull.null]; 229 | } 230 | va_end(ap); 231 | 232 | return [self _initWithTarget:target selector:selector args:args signature:signature]; 233 | } 234 | 235 | - (id _Nonnull)initWithTarget:(id _Nonnull)target selector:(SEL _Nonnull)selector args:(NSArray *_Nullable)args { 236 | NSAssert(target && selector, @"target and selector must not be nil"); 237 | 238 | NSMethodSignature *signature = [self.class signatureForTarget:target selector:selector]; 239 | [self.class checkGeneratorSignature:signature is_block:NO]; 240 | 241 | return [self _initWithTarget:target selector:selector args:args signature:signature]; 242 | } 243 | 244 | - (id)_initWithTarget:(id)target selector:(SEL)selector args:(NSArray *)args signature:(NSMethodSignature *)signature { 245 | if (self = [self init]) { 246 | _target = [target retain]; 247 | _selector = selector; 248 | _signature = signature.retain; 249 | _args = [args copy]; 250 | } 251 | return self; 252 | } 253 | 254 | - (id _Nonnull)initWithBlock:(id _Nonnull)block, ... { 255 | NSAssert(block, @"block must not be nil"); 256 | 257 | NSMethodSignature *signature = NSMethodSignatureForBlock(block); 258 | [self.class checkGeneratorSignature:signature is_block:YES]; 259 | 260 | NSMutableArray *args = [NSMutableArray array]; 261 | va_list ap; 262 | va_start(ap, block); 263 | for (int i=0; i < (int)signature.numberOfArguments - 1; ++i) { 264 | id arg = va_arg(ap, id); 265 | [args addObject:arg ?: NSNull.null]; 266 | } 267 | va_end(ap); 268 | 269 | return [self _initWithBlock:block args:args signature:signature]; 270 | } 271 | 272 | - (id _Nonnull)initWithBlock:(id _Nullable (^ _Nonnull)(id _Nullable))block arg:(id _Nullable)arg { 273 | NSAssert(block, @"block must not be nil"); 274 | 275 | NSMethodSignature *signature = NSMethodSignatureForBlock(block); 276 | [self.class checkGeneratorSignature:signature is_block:YES]; 277 | 278 | return [self _initWithBlock:block args:arg ? @[arg] : @[] signature:signature]; 279 | } 280 | 281 | - (id _Nonnull)initWithStandardBlock:(dispatch_block_t _Nonnull)block { 282 | return [self initWithBlock:(id)block arg:nil]; 283 | } 284 | 285 | - (id)_initWithBlock:(id _Nonnull)block args:(NSArray *_Nullable)args signature:(NSMethodSignature *)signature { 286 | if (self = [self init]) { 287 | _block = [block copy]; 288 | _signature = signature.retain; 289 | _args = [args copy]; 290 | } 291 | return self; 292 | } 293 | 294 | + (NSMethodSignature *)signatureForTarget:(id)target selector:(SEL)selector { 295 | Method m = NULL; 296 | //生成器是类方法 297 | if (object_isClass(target)) { 298 | Class cls = (Class)target; 299 | m = class_getClassMethod(cls, selector); 300 | } 301 | //生成器是实例方法 302 | else { 303 | Class cls = [target class]; 304 | m = class_getInstanceMethod(cls, selector); 305 | } 306 | const char *encoding = method_getTypeEncoding(m); 307 | NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:encoding]; 308 | return signature; 309 | } 310 | 311 | + (void)checkGeneratorSignature:(NSMethodSignature *)signature is_block:(BOOL)is_block{ 312 | //返回值必须是id或者void 313 | __unused BOOL ret_valid = signature.methodReturnType[0] == 'v' || signature.methodReturnType[0] == '@'; 314 | NSAssert(ret_valid, @"return type of generator must be id or void"); 315 | BOOL args_valid = YES; 316 | if (is_block) { 317 | //block最多支持8个参数,block调用默认有第一个参数:block自身 318 | NSAssert(signature.numberOfArguments <= 9, @"arguments count of block must <= 8"); 319 | //所有参数必须为对象类型 320 | if (signature.numberOfArguments > 1) { 321 | for (int i=1; i < signature.numberOfArguments; ++i) { 322 | if ([signature getArgumentTypeAtIndex:i][0] != '@') { 323 | args_valid = NO; 324 | break; 325 | } 326 | } 327 | } 328 | } 329 | else { 330 | //方法调用最多支持8个参数,方法调用默认有第一个参数self(target),第二个参数_cmd(selector) 331 | NSAssert(signature.numberOfArguments <= 10, @"arguments count of method must <= 8"); 332 | //所有参数必须为对象类型 333 | if (signature.numberOfArguments > 2) { 334 | for (int i=2; i < signature.numberOfArguments; ++i) { 335 | if ([signature getArgumentTypeAtIndex:i][0] != '@') { 336 | args_valid = NO; 337 | break; 338 | } 339 | } 340 | } 341 | } 342 | 343 | NSAssert(args_valid, @"argument type of generator must all be id"); 344 | } 345 | 346 | 347 | - (RJResult *)next { 348 | return [self next:nil set_value:NO]; 349 | } 350 | 351 | - (RJResult *)next:(id)value { 352 | return [self next:value set_value:YES]; 353 | } 354 | 355 | - (RJResult *)next:(id)value set_value:(BOOL)set_value { 356 | if (_done) { 357 | return [RJResult resultWithValue:_value error:_error done:_done]; 358 | } 359 | 360 | [RJIteratorStack push:self]; 361 | 362 | //设置跳转返回点 363 | int leave_value = setjmp(_ev_leave); 364 | //非跳转返回 365 | if (leave_value == 0) { 366 | //已经设置了生成器进入点 367 | if (_ev_entry_valid) { 368 | //直接从生成器进入点进入 369 | if (set_value) { 370 | self.value = value; 371 | } 372 | longjmp(_ev_entry, JMP_CONTINUE); 373 | } 374 | else { 375 | //wrapper进入 376 | 377 | //next栈会销毁,所以为wrapper启用新栈 378 | intptr_t sp = (intptr_t)(_stack + _stack_size); 379 | //预留安全空间,防止直接move [sp] 传参 以及msgsend向上访问堆栈 380 | sp -= 256; 381 | //对齐sp 382 | sp &= ~0x07; 383 | 384 | #if defined(__arm__) 385 | asm volatile("mov sp, %0" : : "r"(sp)); 386 | #elif defined(__arm64__) 387 | asm volatile("mov sp, %0" : : "r"(sp)); 388 | #elif defined(__i386__) 389 | asm volatile("movl %0, %%esp" : : "r"(sp)); 390 | #elif defined(__x86_64__) 391 | asm volatile("movq %0, %%rsp" : : "r"(sp)); 392 | #endif 393 | //在新栈上调用wrapper,至此可以认为wrapper,以及生成器函数的运行栈和next无关 394 | [self wrapper]; 395 | } 396 | } 397 | //生成器内部跳转返回 398 | else if (leave_value == JMP_CONTINUE) { 399 | //还可以继续迭代 400 | } 401 | //生成器wrapper跳转返回 402 | else if (leave_value == JMP_DONE) { 403 | //生成器结束,迭代完成 404 | _done = YES; 405 | } 406 | 407 | [RJIteratorStack pop]; 408 | 409 | return [RJResult resultWithValue:_value error:_error done:_done]; 410 | } 411 | 412 | 413 | - (void)wrapper { 414 | id value = nil; 415 | if (_func) { 416 | value = _func(arg_or_nil(_args.firstObject)); 417 | } 418 | else if (_target && _selector) { 419 | id arg0 = _signature.numberOfArguments > 2 ? arg_or_nil(_args[0]) : nil; 420 | id arg1 = _signature.numberOfArguments > 3 ? arg_or_nil(_args[1]) : nil; 421 | id arg2 = _signature.numberOfArguments > 4 ? arg_or_nil(_args[2]) : nil; 422 | id arg3 = _signature.numberOfArguments > 5 ? arg_or_nil(_args[3]) : nil; 423 | id arg4 = _signature.numberOfArguments > 6 ? arg_or_nil(_args[4]) : nil; 424 | id arg5 = _signature.numberOfArguments > 7 ? arg_or_nil(_args[5]) : nil; 425 | id arg6 = _signature.numberOfArguments > 8 ? arg_or_nil(_args[6]) : nil; 426 | id arg7 = _signature.numberOfArguments > 9 ? arg_or_nil(_args[7]) : nil; 427 | if (_signature.methodReturnType[0] == 'v') { 428 | ((void (*)(id, SEL, id, id, id, id, id, id, id, id))objc_msgSend)(_target, _selector, 429 | arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7 430 | ); 431 | 432 | } 433 | else { 434 | value = ((id (*)(id, SEL, id, id, id, id, id, id, id, id))objc_msgSend)(_target, _selector, 435 | arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7 436 | ); 437 | } 438 | } 439 | else if (_block) { 440 | id arg0 = _signature.numberOfArguments > 1 ? arg_or_nil(_args[0]) : nil; 441 | id arg1 = _signature.numberOfArguments > 2 ? arg_or_nil(_args[1]) : nil; 442 | id arg2 = _signature.numberOfArguments > 3 ? arg_or_nil(_args[2]) : nil; 443 | id arg3 = _signature.numberOfArguments > 4 ? arg_or_nil(_args[3]) : nil; 444 | id arg4 = _signature.numberOfArguments > 5 ? arg_or_nil(_args[4]) : nil; 445 | id arg5 = _signature.numberOfArguments > 6 ? arg_or_nil(_args[5]) : nil; 446 | id arg6 = _signature.numberOfArguments > 7 ? arg_or_nil(_args[6]) : nil; 447 | id arg7 = _signature.numberOfArguments > 8 ? arg_or_nil(_args[7]) : nil; 448 | 449 | if (_signature.methodReturnType[0] == 'v') { 450 | ((void (^)(id, id, id, id, id, id, id, id))_block)(arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7); 451 | } 452 | else { 453 | value = ((id (^)(id, id, id, id, id, id, id, id))_block)(arg0,arg1,arg2,arg3,arg4,arg5,arg6,arg7); 454 | } 455 | } 456 | //从生成器返回,说明生成器完全执行结束 457 | //直接返回到迭代器设置的返回点 458 | self.value = value; 459 | 460 | longjmp(_ev_leave, JMP_DONE); 461 | //不会到此 462 | assert(0); 463 | } 464 | 465 | - (id)yield:(id)value { 466 | id yield_value = value; 467 | if ([value isKindOfClass:self.class]) { 468 | //嵌套的迭代器 469 | self.nest = (RJIterator *)value; 470 | } 471 | 472 | next: { 473 | RJResult * result = [self.nest next]; 474 | if (result) { 475 | yield_value = result.value; 476 | } 477 | 478 | _ev_entry_valid = YES; 479 | if (setjmp(_ev_entry) == 0) { 480 | self.value = yield_value; 481 | longjmp(_ev_leave, JMP_CONTINUE); 482 | } 483 | } 484 | 485 | //嵌套迭代器还可继续 486 | if (self.nest && !self.nest.done) { 487 | goto next; 488 | } 489 | 490 | self.nest = nil; 491 | 492 | return self.value; 493 | } 494 | 495 | @end 496 | 497 | 498 | id rj_yield(id value) { 499 | RJIterator *iterator = [RJIteratorStack top]; 500 | return [iterator yield: value]; 501 | } 502 | 503 | RJResult * _Nonnull rj_await(id _Nullable value) { 504 | return (RJResult *) rj_yield(value); 505 | } 506 | 507 | @protocol LikePromise 508 | - (id __nonnull (^ __nonnull)(id __nonnull))then; 509 | - (id __nonnull(^ __nonnull)(id __nonnull))catch; 510 | @end 511 | 512 | 513 | RJAsyncEpilog * rj_async(dispatch_block_t block) { 514 | RJIterator * iterator = [[RJIterator alloc] initWithStandardBlock:block]; 515 | RJAsyncEpilog * epilog = [[RJAsyncEpilog alloc] init]; 516 | RJResult * __block result = nil; 517 | 518 | dispatch_block_t __block step; 519 | step = ^{ 520 | if (!result.done) { 521 | id value = result.value; 522 | //oc闭包 523 | if ([value isKindOfClass:NSClassFromString(@"__NSGlobalBlock__")] || 524 | [value isKindOfClass:NSClassFromString(@"__NSStackBlock__")] || 525 | [value isKindOfClass:NSClassFromString(@"__NSMallocBlock__")] 526 | ) { 527 | ((RJAsyncClosure)value)(^(id value, id error) { 528 | dispatch_async(dispatch_get_main_queue(), ^{ 529 | [result release]; 530 | result = [iterator next: [RJResult resultWithValue:value error:error done:NO]].retain; 531 | step(); 532 | }); 533 | }); 534 | } 535 | //swift 闭包 536 | else if (NSClassFromString(@"_SwiftValue") && 537 | [value isKindOfClass:NSClassFromString(@"_SwiftValue")] && 538 | [[value description] containsString:@"(Function)"] 539 | ) { 540 | [RJAsyncClosureCaller callWithClosure:value finish:^(id _Nullable value, id _Nullable error) { 541 | dispatch_async(dispatch_get_main_queue(), ^{ 542 | [result release]; 543 | result = [iterator next: [RJResult resultWithValue:value error:error done:NO]].retain; 544 | step(); 545 | }); 546 | }]; 547 | } 548 | //AnyPromise 549 | else if (NSClassFromString(@"AnyPromise") && 550 | [value isKindOfClass:NSClassFromString(@"AnyPromise")] && 551 | [value respondsToSelector:@selector(then)] && 552 | [value respondsToSelector:@selector(catch)] 553 | ) { 554 | id promise = (id )value; 555 | void (^__block then_block)(id) = NULL; 556 | void (^__block catch_block)(id) = NULL; 557 | 558 | then_block = Block_copy(^(id value){ 559 | if (then_block) { Block_release(then_block); then_block = NULL; } 560 | if (catch_block) { Block_release(catch_block); catch_block = NULL; } 561 | 562 | [result release]; 563 | result = [iterator next: [RJResult resultWithValue:value error:nil done:NO]].retain; 564 | step(); 565 | }); 566 | 567 | catch_block = Block_copy(^(id error){ 568 | if (then_block) { Block_release(then_block); then_block = NULL; } 569 | if (catch_block) { Block_release(catch_block); catch_block = NULL; } 570 | 571 | [result release]; 572 | result = [iterator next: [RJResult resultWithValue:nil error:error done:NO]].retain; 573 | step(); 574 | }); 575 | 576 | promise.then(then_block).catch(catch_block); 577 | } 578 | //普通对象 579 | else { 580 | RJResult *old_result = result; 581 | result = [iterator next: old_result].retain; 582 | [old_result release]; 583 | 584 | step(); 585 | } 586 | } 587 | else { 588 | [epilog do_finally]; 589 | 590 | [epilog release]; 591 | Block_release(step); 592 | [result release]; 593 | [iterator release]; 594 | } 595 | }; 596 | 597 | step = Block_copy(step); 598 | 599 | dispatch_async(dispatch_get_main_queue(), ^{ 600 | result = iterator.next.retain; 601 | step(); 602 | }); 603 | 604 | return epilog.retain.autorelease; 605 | } 606 | 607 | 608 | 609 | /////////////////////////////////////////////////////////////////////// 610 | /////////////////////////////////////////////////////////////////////// 611 | /* 612 | 获取block签名 613 | 以下代码片段 拷贝 自PromiseKit项目NSMethodSignatureForBlock.m文件,用以获取block签名 614 | https://github.com/mxcl/PromiseKit/blob/master/Sources/NSMethodSignatureForBlock.m 615 | //如果和PromiseKit一起编译,不会冲突,这里全是局部类型/变量 616 | */ 617 | /////////////////////////////////////////////////////////////////////// 618 | /////////////////////////////////////////////////////////////////////// 619 | 620 | struct PMKBlockLiteral { 621 | void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock 622 | int flags; 623 | int reserved; 624 | void (*invoke)(void *, ...); 625 | struct block_descriptor { 626 | unsigned long int reserved; // NULL 627 | unsigned long int size; // sizeof(struct Block_literal_1) 628 | // optional helper functions 629 | void (*copy_helper)(void *dst, void *src); // IFF (1<<25) 630 | void (*dispose_helper)(void *src); // IFF (1<<25) 631 | // required ABI.2010.3.16 632 | const char *signature; // IFF (1<<30) 633 | } *descriptor; 634 | // imported variables 635 | }; 636 | 637 | typedef NS_OPTIONS(NSUInteger, PMKBlockDescriptionFlags) { 638 | PMKBlockDescriptionFlagsHasCopyDispose = (1 << 25), 639 | PMKBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code 640 | PMKBlockDescriptionFlagsIsGlobal = (1 << 28), 641 | PMKBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE 642 | PMKBlockDescriptionFlagsHasSignature = (1 << 30) 643 | }; 644 | 645 | // It appears 10.7 doesn't support quotes in method signatures. Remove them 646 | // via @rabovik's method. See https://github.com/OliverLetterer/SLObjectiveCRuntimeAdditions/pull/2 647 | #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8 648 | NS_INLINE static const char * pmk_removeQuotesFromMethodSignature(const char *str){ 649 | char *result = malloc(strlen(str) + 1); 650 | BOOL skip = NO; 651 | char *to = result; 652 | char c; 653 | while ((c = *str++)) { 654 | if ('"' == c) { 655 | skip = !skip; 656 | continue; 657 | } 658 | if (skip) continue; 659 | *to++ = c; 660 | } 661 | *to = '\0'; 662 | return result; 663 | } 664 | #endif 665 | 666 | static NSMethodSignature *NSMethodSignatureForBlock(id block) { 667 | if (!block) 668 | return nil; 669 | 670 | struct PMKBlockLiteral *blockRef = (__bridge struct PMKBlockLiteral *)block; 671 | PMKBlockDescriptionFlags flags = (PMKBlockDescriptionFlags)blockRef->flags; 672 | 673 | if (flags & PMKBlockDescriptionFlagsHasSignature) { 674 | void *signatureLocation = blockRef->descriptor; 675 | signatureLocation += sizeof(unsigned long int); 676 | signatureLocation += sizeof(unsigned long int); 677 | 678 | if (flags & PMKBlockDescriptionFlagsHasCopyDispose) { 679 | signatureLocation += sizeof(void(*)(void *dst, void *src)); 680 | signatureLocation += sizeof(void (*)(void *src)); 681 | } 682 | 683 | const char *signature = (*(const char **)signatureLocation); 684 | #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8 685 | signature = pmk_removeQuotesFromMethodSignature(signature); 686 | NSMethodSignature *nsSignature = [NSMethodSignature signatureWithObjCTypes:signature]; 687 | free((void *)signature); 688 | 689 | return nsSignature; 690 | #endif 691 | return [NSMethodSignature signatureWithObjCTypes:signature]; 692 | } 693 | return 0; 694 | } 695 | 696 | -------------------------------------------------------------------------------- /RJIterator/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 | 35 | NSAppTransportSecurity 36 | 37 | NSAllowsArbitraryLoads 38 | 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /RJIterator/RJIterator-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | 6 | #import "RJIterator.h" 7 | -------------------------------------------------------------------------------- /RJIterator/Tests.h: -------------------------------------------------------------------------------- 1 | // 2 | // Tests.h 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/13. 6 | // Copyright © 2018年 JK. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface Tests : NSObject 12 | + (void)verboseTest; 13 | @end 14 | -------------------------------------------------------------------------------- /RJIterator/Tests.m: -------------------------------------------------------------------------------- 1 | // 2 | // Tests.m 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/13. 6 | // Copyright © 2018年 JK. All rights reserved. 7 | // 8 | 9 | #import "Tests.h" 10 | #import 11 | #import "RJIterator.h" 12 | 13 | static NSString* talk(NSString *name) { 14 | rj_yield([NSString stringWithFormat:@"Hello %@, How are you?", name]); 15 | rj_yield(@"Today is Friday"); 16 | rj_yield(@"So yestday is Thursday"); 17 | rj_yield(@"And tomorrow is Saturday"); 18 | rj_yield(@"Over"); 19 | return @"==talk done=="; 20 | } 21 | 22 | @implementation Tests 23 | 24 | + (void)verboseTest { 25 | [self test1]; 26 | [self test2]; 27 | [self test3]; 28 | [self test4]; 29 | [self test5]; 30 | [self test6]; 31 | [self test7]; 32 | [self test8]; 33 | } 34 | 35 | 36 | + (void)test1 { 37 | NSLog(@"************************ Begin %s *******************************", __func__); 38 | RJIterator *it = nil; 39 | RJResult *r = nil; 40 | 41 | it = [[RJIterator alloc] initWithFunc:talk arg:@"乌卡卡"]; 42 | r = [it next]; 43 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 44 | r = [it next]; 45 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 46 | r = [it next]; 47 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 48 | r = [it next]; 49 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 50 | r = [it next]; 51 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 52 | r = [it next]; 53 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 54 | r = [it next]; 55 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 56 | 57 | NSLog(@"************************ End %s *******************************", __func__); 58 | } 59 | 60 | - (NSNumber *)Fibonacci { 61 | int prev = 0; 62 | int cur = 1; 63 | for (;;) { 64 | rj_yield(@(cur)); 65 | 66 | int p = prev; 67 | prev = cur; 68 | cur = p + cur; 69 | 70 | if (cur > 6765) { 71 | break; 72 | } 73 | } 74 | return @(cur); 75 | } 76 | 77 | + (void)test2 { 78 | NSLog(@"************************ Begin %s *******************************", __func__); 79 | RJIterator *it = nil; 80 | RJResult *r = nil; 81 | 82 | it = [[RJIterator alloc] initWithTarget:[self new] selector:@selector(Fibonacci)]; 83 | for (int i = 0; i < 22; ++i) { 84 | r = [it next]; 85 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 86 | } 87 | 88 | NSLog(@"************************ End %s *******************************", __func__); 89 | } 90 | 91 | //迭代器嵌套 92 | - (id)dataBox:(NSString *)name age:(NSNumber *)age { 93 | NSLog(@"==in dataBox/enter"); 94 | rj_yield([NSString stringWithFormat:@"Hello, I know you name:%@, age:%@, you want some data", name, age]); 95 | rj_yield(@"Fibonacci:"); 96 | NSLog(@"==in dataBox/will return Fibonacci"); 97 | rj_yield([[RJIterator alloc] initWithTarget:self selector:@selector(Fibonacci)]); 98 | rj_yield(@"Random Data:"); 99 | NSLog(@"==in dataBox/will return Random Data"); 100 | rj_yield(@"🐶"); 101 | rj_yield([NSArray new]); 102 | rj_yield(@12345); 103 | rj_yield(self); 104 | return @"dataBox Over"; 105 | } 106 | 107 | //更深嵌套 108 | - (id)dataBox2:(NSString *)name age:(NSNumber *)age { 109 | rj_yield([[RJIterator alloc] initWithTarget:self selector:@selector(dataBox:age:), name, age]); 110 | 111 | NSLog(@"==in dataBox2/enter"); 112 | rj_yield([NSString stringWithFormat:@"Hello, I know you name:%@, age:%@, you want some data", name, age]); 113 | rj_yield(@"Fibonacci:"); 114 | NSLog(@"==in dataBox2/will return Fibonacci"); 115 | rj_yield([[RJIterator alloc] initWithTarget:self selector:@selector(Fibonacci)]); 116 | rj_yield(@"Random Data:"); 117 | NSLog(@"==in dataBox2/will return Random Data"); 118 | rj_yield(@"🐶"); 119 | rj_yield([NSArray new]); 120 | rj_yield(@12345); 121 | rj_yield(self); 122 | return @"dataBox2 Over"; 123 | } 124 | 125 | + (void)test3 { 126 | NSLog(@"************************ Begin %s *******************************", __func__); 127 | RJIterator *it = nil; 128 | RJResult *r = nil; 129 | 130 | it = [[RJIterator alloc] initWithTarget:[self new] selector:@selector(dataBox:age:), @"大表哥", @28]; 131 | do { 132 | r = [it next]; 133 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 134 | }while(!r.done); 135 | 136 | NSLog(@"************************ End %s *******************************", __func__); 137 | } 138 | 139 | + (void)test4 { 140 | NSLog(@"************************ Begin %s *******************************", __func__); 141 | RJIterator *it = nil; 142 | RJResult *r = nil; 143 | 144 | it = [[RJIterator alloc] initWithTarget:[self new] selector:@selector(dataBox2:age:), @"古德曼", @30]; 145 | do { 146 | r = [it next]; 147 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 148 | }while(!r.done); 149 | 150 | NSLog(@"************************ End %s *******************************", __func__); 151 | } 152 | 153 | + (void)test5 { 154 | NSLog(@"************************ Begin %s *******************************", __func__); 155 | RJIterator *it = nil; 156 | RJResult *r = nil; 157 | 158 | it = [[RJIterator alloc] initWithBlock:^{ 159 | rj_yield(@100); 160 | rj_yield(@101); 161 | rj_yield(@102); 162 | rj_yield(@103); 163 | }]; 164 | 165 | do { 166 | r = [it next]; 167 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 168 | }while(!r.done); 169 | 170 | NSLog(@"************************"); 171 | 172 | it = [[RJIterator alloc] initWithBlock:^(NSString *name, NSNumber *age) { 173 | rj_yield([NSString stringWithFormat:@"Hello %@, your age is:%@", name, age]); 174 | rj_yield(@100); 175 | rj_yield(@101); 176 | rj_yield(@102); 177 | rj_yield(@103); 178 | NSLog(@"==in block/block done"); 179 | }, @"索尔", @33]; 180 | do { 181 | r = [it next]; 182 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 183 | }while(!r.done); 184 | 185 | NSLog(@"************************ End %s *******************************", __func__); 186 | } 187 | 188 | + (void)test6 { 189 | NSLog(@"************************ Begin %s *******************************", __func__); 190 | RJIterator *it = nil; 191 | RJResult *r = nil; 192 | 193 | it = [[RJIterator alloc] initWithBlock:^(NSString *name, NSNumber *age) { 194 | rj_yield([NSString stringWithFormat:@"Hello %@, your age is:%@", name, age]); 195 | rj_yield(@100); 196 | rj_yield(@101); 197 | rj_yield(@102); 198 | rj_yield(@103); 199 | return @"i tell you : block done"; 200 | }, @"索尔", @33]; 201 | do { 202 | r = [it next]; 203 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 204 | }while(!r.done); 205 | 206 | NSLog(@"************************ End %s *******************************", __func__); 207 | } 208 | 209 | + (NSNumber *)ClassFibonacci { 210 | int prev = 0; 211 | int cur = 1; 212 | for (;;) { 213 | rj_yield(@(cur)); 214 | 215 | int p = prev; 216 | prev = cur; 217 | cur = p + cur; 218 | 219 | if (cur > 6765) { 220 | break; 221 | } 222 | } 223 | return @(cur); 224 | } 225 | 226 | + (void)test7 { 227 | NSLog(@"************************ Begin %s *******************************", __func__); 228 | RJIterator *it = nil; 229 | RJResult *r = nil; 230 | 231 | it = [[RJIterator alloc] initWithTarget:self selector:@selector(ClassFibonacci)]; 232 | for (int i = 0; i < 22; ++i) { 233 | r = [it next]; 234 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 235 | } 236 | 237 | NSLog(@"************************ End %s *******************************", __func__); 238 | } 239 | 240 | + (void)talk2:(NSString *)name { 241 | NSString *really_name = rj_yield([NSString stringWithFormat:@"FakeName: %@", name]); 242 | NSLog(@"== talk2/really_name: %@", really_name); 243 | } 244 | 245 | + (void)test8 { 246 | NSLog(@"************************ Begin %s *******************************", __func__); 247 | RJIterator *it = nil; 248 | RJResult *r = nil; 249 | 250 | it = [[RJIterator alloc] initWithTarget:self selector:@selector(talk2:), @"第一帅"]; 251 | r = [it next]; 252 | NSLog(@"== value: %@, done:%@", r.value, r.done ? @"YES" : @"NO"); 253 | 254 | //为next传参,将在rj_yield返回前改变返回值, 即修改really_name 255 | r = [it next:@"RJK"]; //打印 RJK 256 | //如果不传参,将打印 //FakeName: 第一帅 257 | 258 | NSLog(@"************************ End %s *******************************", __func__); 259 | } 260 | 261 | @end 262 | -------------------------------------------------------------------------------- /RJIterator/TestsSwift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestsSwift.swift 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/14. 6 | // Copyright © 2018年 renjinkui. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | fileprivate func talk(name: Any?) -> Any? { 12 | var cmd = "" 13 | repeat { 14 | rj_yield("Hello \(name ?? ""), How are you?"); 15 | rj_yield("Today is Friday"); 16 | rj_yield("So yestday is Thursday"); 17 | rj_yield("And tomorrow is Saturday"); 18 | cmd = rj_yield("Over") as? String ?? ""; 19 | }while cmd != "Over" 20 | 21 | return "==talk done=="; 22 | } 23 | 24 | func count_1_3(_: Any?) -> Any? { 25 | rj_yield(1) 26 | rj_yield(2) 27 | return 3 28 | } 29 | func count_4_5(_: Any?) -> Any? { 30 | rj_yield(4) 31 | rj_yield(5) 32 | return 6 33 | } 34 | func count_7_9(_: Any?) -> Any? { 35 | rj_yield(7) 36 | rj_yield(8) 37 | return 9 38 | } 39 | 40 | func count(_: Any?) -> Any? { 41 | rj_yield(RJIterator.init(withFunc: count_1_3, arg: nil)) 42 | rj_yield(RJIterator.init(withFunc: count_4_5, arg: nil)) 43 | rj_yield(RJIterator.init(withFunc: count_7_9, arg: nil)) 44 | return nil 45 | } 46 | 47 | class TestsSwift: NSObject { 48 | 49 | static func verboseTests() { 50 | test0() 51 | test1() 52 | test2() 53 | test3() 54 | test4() 55 | test5() 56 | test6() 57 | test7() 58 | TestsSwift.init().onLogin() 59 | } 60 | 61 | deinit { 62 | print("== TestsSwift deint") 63 | } 64 | 65 | static func test0() { 66 | print("************************ Begin test0 *******************************"); 67 | var it: RJIterator; 68 | var r: RJResult; 69 | 70 | it = RJIterator.init(withFunc: count, arg:nil) 71 | r = it.next() 72 | print("value: \(r.value), done:\(r.done)") 73 | r = it.next() 74 | print("value: \(r.value), done:\(r.done)") 75 | r = it.next() 76 | print("value: \(r.value), done:\(r.done)") 77 | r = it.next() 78 | print("value: \(r.value), done:\(r.done)") 79 | r = it.next() 80 | print("value: \(r.value), done:\(r.done)") 81 | r = it.next("again") 82 | print("value: \(r.value), done:\(r.done)") 83 | r = it.next() 84 | print("value: \(r.value), done:\(r.done)") 85 | r = it.next("again") 86 | print("value: \(r.value), done:\(r.done)") 87 | r = it.next() 88 | print("value: \(r.value), done:\(r.done)") 89 | r = it.next("again") 90 | print("value: \(r.value), done:\(r.done)") 91 | r = it.next() 92 | print("value: \(r.value), done:\(r.done)") 93 | r = it.next("again") 94 | print("value: \(r.value), done:\(r.done)") 95 | r = it.next() 96 | print("value: \(r.value), done:\(r.done)") 97 | r = it.next("again") 98 | print("value: \(r.value), done:\(r.done)") 99 | r = it.next() 100 | print("value: \(r.value), done:\(r.done)") 101 | print("************************ End test0 *******************************"); 102 | } 103 | 104 | 105 | static func test1() { 106 | print("************************ Begin test1 *******************************"); 107 | var it: RJIterator; 108 | var r: RJResult; 109 | 110 | it = RJIterator.init(withFunc: talk, arg: "爱德华") 111 | r = it.next() 112 | print("value: \(r.value), done:\(r.done)") 113 | r = it.next() 114 | print("value: \(r.value), done:\(r.done)") 115 | r = it.next() 116 | print("value: \(r.value), done:\(r.done)") 117 | r = it.next() 118 | print("value: \(r.value), done:\(r.done)") 119 | r = it.next() 120 | print("value: \(r.value), done:\(r.done)") 121 | r = it.next("again") 122 | print("value: \(r.value), done:\(r.done)") 123 | r = it.next() 124 | print("value: \(r.value), done:\(r.done)") 125 | r = it.next("again") 126 | print("value: \(r.value), done:\(r.done)") 127 | r = it.next() 128 | print("value: \(r.value), done:\(r.done)") 129 | r = it.next("again") 130 | print("value: \(r.value), done:\(r.done)") 131 | r = it.next() 132 | print("value: \(r.value), done:\(r.done)") 133 | r = it.next("again") 134 | print("value: \(r.value), done:\(r.done)") 135 | r = it.next() 136 | print("value: \(r.value), done:\(r.done)") 137 | r = it.next("again") 138 | print("value: \(r.value), done:\(r.done)") 139 | r = it.next() 140 | print("value: \(r.value), done:\(r.done)") 141 | print("************************ End test1 *******************************"); 142 | } 143 | 144 | @objc func Fibonacci() -> NSNumber { 145 | var prev = 0; 146 | var cur = 1; 147 | while(true) { 148 | rj_yield(cur); 149 | 150 | let p = prev; 151 | prev = cur; 152 | cur = p + cur; 153 | 154 | if (cur > 6765) { 155 | break; 156 | } 157 | } 158 | return (cur as NSNumber); 159 | } 160 | 161 | static func test2() { 162 | print("************************ Begin test2 *******************************"); 163 | var it: RJIterator; 164 | var r: RJResult; 165 | 166 | it = RJIterator.init(target: TestsSwift.init(), selector: #selector(Fibonacci), args: nil) 167 | 168 | repeat { 169 | r = it.next() 170 | print("value: \(r.value), done:\(r.done)") 171 | }while(!r.done) 172 | 173 | print("************************ End test2 *******************************"); 174 | } 175 | 176 | 177 | //迭代器嵌套 178 | @objc func dataBox(name: String, age: NSNumber) -> String { 179 | print("==in dataBox/enter"); 180 | rj_yield("Hello, I know you name:\(name), age:\(age), you want some data"); 181 | rj_yield("Fibonacci:"); 182 | print("==in dataBox/will return Fibonacci"); 183 | rj_yield(RJIterator.init(target: self, selector: #selector(Fibonacci), args: nil)); 184 | rj_yield("Random Data:"); 185 | print("==in dataBox/will return Random Data"); 186 | rj_yield("🐶"); 187 | rj_yield([]); 188 | rj_yield(12345 as NSNumber); 189 | rj_yield(self); 190 | return "dataBox Over"; 191 | } 192 | 193 | 194 | @objc func dataBox2(name: String, age: NSNumber) -> String { 195 | //更深嵌套 196 | rj_yield(RJIterator.init(target: TestsSwift.init(), selector: #selector(dataBox(name:age:)), args: ["RJK", 28])) 197 | 198 | print("==in dataBox2/enter"); 199 | rj_yield("Hello, I know you name:\(name), age:\(age), you want some data"); 200 | rj_yield("Fibonacci:"); 201 | print("==in dataBox2/will return Fibonacci"); 202 | rj_yield(RJIterator.init(target: self, selector: #selector(Fibonacci), args: nil)); 203 | rj_yield("Random Data:"); 204 | print("==in dataBox2/will return Random Data"); 205 | rj_yield("🐶"); 206 | rj_yield([]); 207 | rj_yield(12345 as NSNumber); 208 | rj_yield(self); 209 | return "dataBox2 Over"; 210 | } 211 | 212 | 213 | static func test3() { 214 | print("************************ Begin test3 *******************************"); 215 | var it: RJIterator; 216 | var r: RJResult; 217 | 218 | it = RJIterator.init(target: TestsSwift.init(), selector: #selector(dataBox(name:age:)), args: ["RJK", 28]); 219 | 220 | repeat { 221 | r = it.next() 222 | print("value: \(r.value), done:\(r.done)") 223 | }while(!r.done) 224 | print("************************ End test3 *******************************"); 225 | } 226 | 227 | static func test4() { 228 | print("************************ Begin test4 *******************************"); 229 | var it: RJIterator; 230 | var r: RJResult; 231 | 232 | it = RJIterator.init(target: TestsSwift.init(), selector: #selector(dataBox2(name:age:)), args: ["Walt White", 48]); 233 | 234 | repeat { 235 | r = it.next() 236 | print("value: \(r.value), done:\(r.done)") 237 | }while(!r.done) 238 | print("************************ End test4 *******************************"); 239 | } 240 | 241 | static func test5() { 242 | print("************************ Begin test5 *******************************"); 243 | var it: RJIterator; 244 | var r: RJResult; 245 | 246 | it = RJIterator.init(standardBlock: { 247 | print("Hello"); 248 | rj_yield("🐶"); 249 | rj_yield([]); 250 | rj_yield(12345 as NSNumber); 251 | rj_yield(self); 252 | }) 253 | 254 | repeat { 255 | r = it.next() 256 | print("value: \(r.value), done:\(r.done)") 257 | }while(!r.done) 258 | 259 | it = RJIterator.init(block: { (name) -> Any? in 260 | print("Hello \(name)"); 261 | rj_yield("🐶"); 262 | rj_yield([]); 263 | rj_yield(12345 as NSNumber); 264 | rj_yield(self); 265 | return "Done" 266 | }, arg: "JJK") 267 | 268 | repeat { 269 | r = it.next() 270 | print("value: \(r.value), done:\(r.done)") 271 | }while(!r.done) 272 | print("************************ End test5 *******************************"); 273 | } 274 | 275 | @objc static func ClassFibonacci() -> NSNumber { 276 | var prev = 0; 277 | var cur = 1; 278 | while(true) { 279 | rj_yield(cur); 280 | 281 | let p = prev; 282 | prev = cur; 283 | cur = p + cur; 284 | 285 | if (cur > 6765) { 286 | break; 287 | } 288 | } 289 | return (cur as NSNumber); 290 | } 291 | 292 | static func test6() { 293 | print("************************ Begin test6 *******************************"); 294 | var it: RJIterator; 295 | var r: RJResult; 296 | 297 | it = RJIterator.init(target: TestsSwift.self, selector: #selector(ClassFibonacci), args: nil) 298 | 299 | repeat { 300 | r = it.next() 301 | print("value: \(r.value), done:\(r.done)") 302 | }while(!r.done) 303 | 304 | print("************************ End test6 *******************************"); 305 | } 306 | 307 | @objc static func talk2(name: String) { 308 | let really_name = rj_yield("FakeName: \(name)") 309 | print("==talk2/really_name:\(really_name)") 310 | } 311 | 312 | static func test7() { 313 | print("************************ Begin test7 *******************************"); 314 | var it: RJIterator; 315 | var r: RJResult; 316 | 317 | it = RJIterator.init(target: TestsSwift.self, selector: #selector(talk2(name:)), args: ["第一帅"]) 318 | r = it.next(); 319 | print("value: \(r.value), done:\(r.done)") 320 | r = it.next("JK"); 321 | print("value: \(r.value), done:\(r.done)") 322 | print("************************ End test7 *******************************"); 323 | } 324 | 325 | //登录 326 | func login(account: String, pwd: String) -> RJAsyncClosure { 327 | //返回RJAsyncClosure类型闭包 328 | return { (callback: @escaping RJAsyncCallback) in 329 | //以asyncAfter 模拟Http请求 + 回调 330 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 331 | //登录成功 332 | callback(["uid": "80022", "token":"67625235555"], nil); 333 | }) 334 | }; 335 | } 336 | //查询个人信息 337 | func query(uid:String, token: String) -> RJAsyncClosure { 338 | return { (callback: @escaping RJAsyncCallback) in 339 | //以asyncAfter 模拟Http请求 + 回调 340 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 341 | //查询成功 342 | callback(["name": "JimGreen", "url":"http://oem96wx6v.bkt.clouddn.com/bizhi-1030-1097-2.jpg"], NSError.init(domain: "s2", code: -1, userInfo: nil)); 343 | }) 344 | }; 345 | } 346 | //下载头像 347 | func download(url: String) -> RJAsyncClosure { 348 | return {(callback: @escaping RJAsyncCallback) in 349 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 350 | do { 351 | let data: Data? = try Data.init(contentsOf: URL.init(string: url)!) 352 | let iamge = UIImage.init(data: data!) 353 | //下载成功 354 | callback(iamge, nil) 355 | } catch let error { 356 | //下载失败 357 | callback(nil, error) 358 | } 359 | }) 360 | }; 361 | } 362 | //处理头像 363 | func handle(image: UIImage) -> RJAsyncClosure { 364 | return { (callback: @escaping RJAsyncCallback) in 365 | DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 2, execute: { 366 | //处理成功 367 | callback(image, nil); 368 | }) 369 | }; 370 | } 371 | 372 | @objc func onLogin(_ sender: Any? = nil) { 373 | print("************************ Begin onLogin *******************************"); 374 | rj_async { 375 | var result: RJResult 376 | 377 | print("开始登录") 378 | result = rj_await( self.login(account: "112233", pwd: "445566") ) 379 | if let error = result.error { 380 | print("登录失败:\(error)") 381 | return 382 | } 383 | let login_json = result.value as! [String: String] 384 | print("登录成功, json:\(login_json)") 385 | 386 | print("开始查询信息") 387 | result = rj_await( self.query(uid: login_json["uid"]!, token: login_json["token"]!) ) 388 | if let error = result.error { 389 | print("查询信息失败:\(error)") 390 | return 391 | } 392 | let info_json = result.value as! [String: String] 393 | print("查询信息成功, json:\(info_json)") 394 | 395 | print("开始下载头像") 396 | result = rj_await( self.download(url: info_json["url"]!) ) 397 | if let error = result.error { 398 | print("下载头像失败:\(error)") 399 | return 400 | } 401 | let image = result.value as! UIImage 402 | print("下载头像成功, image:\(image)") 403 | 404 | print("开始处理头像") 405 | result = rj_await( self.handle(image: image) ) 406 | if let error = result.error { 407 | print("处理头像失败:\(error)") 408 | return 409 | } 410 | let beautiful_image = result.value as! UIImage 411 | print("处理头像成功, beautiful_image:\(beautiful_image)") 412 | 413 | print("进入详情界面") 414 | } 415 | .finally { 416 | print("登录收尾") 417 | } 418 | 419 | print("************************ End onLogin *******************************"); 420 | } 421 | 422 | } 423 | -------------------------------------------------------------------------------- /RJIterator/UserInfoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoViewController.h 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/14. 6 | // Copyright © 2018年 renjinkui. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UserInfoViewController : UIViewController 12 | @property (nonatomic, copy) NSString *uid; 13 | @property (nonatomic, copy) NSString *token; 14 | @property (nonatomic, copy) NSString *name; 15 | @property (nonatomic, strong) UIImage *headimg; 16 | @end 17 | -------------------------------------------------------------------------------- /RJIterator/UserInfoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfoViewController.m 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/14. 6 | // Copyright © 2018年 renjinkui. All rights reserved. 7 | // 8 | 9 | #import "UserInfoViewController.h" 10 | 11 | @interface UserInfoViewController () 12 | 13 | @end 14 | 15 | @implementation UserInfoViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view. 20 | self.view.backgroundColor = [UIColor whiteColor]; 21 | 22 | UIButton *close = [UIButton buttonWithType:UIButtonTypeCustom]; 23 | [close setTitle:@"关闭" forState:UIControlStateNormal]; 24 | [close setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 25 | [close addTarget:self action:@selector(close) forControlEvents:UIControlEventTouchUpInside]; 26 | close.frame = CGRectMake(10, 80, 60, 50); 27 | [self.view addSubview:close]; 28 | 29 | 30 | UIImageView *imgv = [[UIImageView alloc] initWithImage:_headimg]; 31 | imgv.frame = CGRectMake(0, 0, 100, 100); 32 | imgv.center = self.view.center; 33 | [self.view addSubview:imgv]; 34 | 35 | UILabel *infoLabel = [UILabel new]; 36 | infoLabel.text = [NSString stringWithFormat:@"Hello: %@, you uid:%@, token:%@", _name, _uid, _token]; 37 | infoLabel.textColor = [UIColor blackColor]; 38 | infoLabel.textAlignment = NSTextAlignmentCenter; 39 | infoLabel.numberOfLines = 0; 40 | infoLabel.frame = CGRectMake(0, CGRectGetMaxY(imgv.frame), self.view.frame.size.width, 80.0); 41 | [self.view addSubview:infoLabel]; 42 | } 43 | 44 | - (void)close { 45 | [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL]; 46 | } 47 | 48 | - (void)didReceiveMemoryWarning { 49 | [super didReceiveMemoryWarning]; 50 | // Dispose of any resources that can be recreated. 51 | } 52 | 53 | /* 54 | #pragma mark - Navigation 55 | 56 | // In a storyboard-based application, you will often want to do a little preparation before navigation 57 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 58 | // Get the new view controller using [segue destinationViewController]. 59 | // Pass the selected object to the new view controller. 60 | } 61 | */ 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /RJIterator/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/13. 6 | // Copyright © 2018年 renjinkui. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /RJIterator/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/13. 6 | // Copyright © 2018年 renjinkui. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Tests.h" 11 | #import "RJIterator-Swift.h" 12 | #import "RJIterator.h" 13 | #import "UserInfoViewController.h" 14 | 15 | @interface ViewController () 16 | @property (nonatomic, strong) UIButton *loginButton; 17 | @property (nonatomic, strong) NSHashTable *ht; 18 | @property (nonatomic, strong) UIViewController *vc; 19 | @end 20 | 21 | @implementation ViewController 22 | 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | _loginButton = [UIButton buttonWithType:UIButtonTypeCustom]; 27 | [_loginButton setTitle:@"登录" forState:UIControlStateNormal]; 28 | [_loginButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 29 | _loginButton.backgroundColor = [UIColor blueColor]; 30 | [_loginButton addTarget:self action:@selector(onLogin:) forControlEvents:UIControlEventTouchUpInside]; 31 | _loginButton.frame = CGRectMake(0, 0, 100, 50); 32 | _loginButton.center = self.view.center; 33 | 34 | [self.view addSubview:_loginButton]; 35 | 36 | // [Tests verboseTest]; 37 | // [TestsSwift verboseTests]; 38 | } 39 | 40 | - (void)dealloc { 41 | NSLog(@"== %@ dealloc", self); 42 | } 43 | 44 | //登录 45 | - (RJAsyncClosure)loginWithAccount:(NSString *)account pwd:(NSString *)pwd { 46 | //返回RJAsyncClosure类型block 47 | return ^(RJAsyncCallback callback){ 48 | //以dispatch_after模拟Http请求 49 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 50 | //登录成功 51 | callback(@{@"uid": @"0001", @"token": @"ffccdd566"}, nil); 52 | }); 53 | }; 54 | } 55 | 56 | //查询信息 57 | - (RJAsyncClosure)queryInfoWithUid:(NSString *)uid token:(NSString *)token{ 58 | return ^(RJAsyncCallback callback){ 59 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 60 | //查询成功 61 | callback(@{@"url": @"http://oem96wx6v.bkt.clouddn.com/bizhi-1030-1097-2.jpg", @"name": @"LiLei"}, 62 | /*[NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Query error, please check network"}]*/nil 63 | ); 64 | }); 65 | }; 66 | } 67 | 68 | //下载头像 69 | - (RJAsyncClosure)downloadHeadImage:(NSString *)url{ 70 | return ^(RJAsyncCallback callback){ 71 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 72 | //下载头像 73 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 74 | callback(data ? [UIImage imageWithData:data] : nil, 75 | data ? nil : [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Download error, please check network"}]); 76 | }); 77 | }; 78 | } 79 | 80 | //处理头像 81 | - (RJAsyncClosure)handle:(UIImage *)image{ 82 | return ^(RJAsyncCallback callback){ 83 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 84 | //处理成功 85 | callback(image, nil); 86 | }); 87 | }; 88 | } 89 | 90 | #define toast NSLog 91 | 92 | - (void)onLogin:(id)sender { 93 | rj_async(^{ 94 | //每次await 的 result 95 | RJResult *result = nil; 96 | 97 | _loginButton.enabled = NO; 98 | [_loginButton setTitle:@"登录中..." forState:UIControlStateNormal]; 99 | 100 | NSLog(@"开始登录..."); 101 | result = rj_await( [self loginWithAccount:@"112233" pwd:@"12345"] ); 102 | if (result.error) { 103 | toast(@"登录失败, error:%@", result.error); 104 | return ; 105 | } 106 | NSDictionary *login_josn = result.value; 107 | NSLog(@"登录完成,json: %@", login_josn); 108 | 109 | NSLog(@"开始拉取个人信息..."); 110 | result = rj_await( [self queryInfoWithUid:login_josn[@"uid"] token:login_josn[@"token"]] ); 111 | if (result.error) { 112 | toast(@"拉取个人信息失败, error:%@", result.error); 113 | return ; 114 | } 115 | NSDictionary *info_josn = result.value; 116 | NSLog(@"拉取个人信息完成,json: %@", info_josn); 117 | 118 | NSLog(@"开始下载头像..."); 119 | result = rj_await( [self downloadHeadImage:info_josn[@"url"]] ); 120 | if (result.error) { 121 | toast(@"下载头像失败, error:%@", result.error); 122 | return ; 123 | } 124 | UIImage *head_image = result.value; 125 | NSLog(@"下载头像完成,head_image: %@", head_image); 126 | 127 | NSLog(@"开始处理头像..."); 128 | result = rj_await( [self handle:head_image] ); 129 | if (result.error) { 130 | toast(@"处理头像失败, error:%@", result.error); 131 | return ; 132 | } 133 | head_image = result.value; 134 | NSLog(@"处理头像完成,head_image: %@", head_image); 135 | 136 | NSLog(@"全部完成,进入详情界面"); 137 | 138 | UserInfoViewController *vc = [[UserInfoViewController alloc] init]; 139 | vc.uid = login_josn[@"uid"]; 140 | vc.token = login_josn[@"token"]; 141 | vc.name = info_josn[@"name"]; 142 | vc.headimg = head_image; 143 | [self presentViewController:vc animated:YES completion:NULL]; 144 | }) 145 | .finally(^{ 146 | NSLog(@"...finally 收尾"); 147 | _loginButton.enabled = YES; 148 | [_loginButton setTitle:@"登录" forState:UIControlStateNormal]; 149 | }); 150 | } 151 | 152 | 153 | 154 | - (void)didReceiveMemoryWarning { 155 | [super didReceiveMemoryWarning]; 156 | // Dispose of any resources that can be recreated. 157 | } 158 | 159 | 160 | @end 161 | -------------------------------------------------------------------------------- /RJIterator/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // RJIterator 4 | // 5 | // Created by renjinkui on 2018/4/13. 6 | // Copyright © 2018年 renjinkui. 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 | 18 | --------------------------------------------------------------------------------