NSThread+YYAdd.m 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. //
  2. // NSThread+YYAdd.h
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 15/7/3.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "NSThread+YYAdd.h"
  12. #import <CoreFoundation/CoreFoundation.h>
  13. @interface NSThread_YYAdd : NSObject @end
  14. @implementation NSThread_YYAdd @end
  15. #if __has_feature(objc_arc)
  16. #error This file must be compiled without ARC. Specify the -fno-objc-arc flag to this file.
  17. #endif
  18. static NSString *const YYNSThreadAutoleasePoolKey = @"YYNSThreadAutoleasePoolKey";
  19. static NSString *const YYNSThreadAutoleasePoolStackKey = @"YYNSThreadAutoleasePoolStackKey";
  20. static const void *PoolStackRetainCallBack(CFAllocatorRef allocator, const void *value) {
  21. return value;
  22. }
  23. static void PoolStackReleaseCallBack(CFAllocatorRef allocator, const void *value) {
  24. CFRelease((CFTypeRef)value);
  25. }
  26. static inline void YYAutoreleasePoolPush() {
  27. NSMutableDictionary *dic = [NSThread currentThread].threadDictionary;
  28. NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];
  29. if (!poolStack) {
  30. /*
  31. do not retain pool on push,
  32. but release on pop to avoid memory analyze warning
  33. */
  34. CFArrayCallBacks callbacks = {0};
  35. callbacks.retain = PoolStackRetainCallBack;
  36. callbacks.release = PoolStackReleaseCallBack;
  37. poolStack = (id)CFArrayCreateMutable(CFAllocatorGetDefault(), 0, &callbacks);
  38. dic[YYNSThreadAutoleasePoolStackKey] = poolStack;
  39. CFRelease(poolStack);
  40. }
  41. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //< create
  42. [poolStack addObject:pool]; // push
  43. }
  44. static inline void YYAutoreleasePoolPop() {
  45. NSMutableDictionary *dic = [NSThread currentThread].threadDictionary;
  46. NSMutableArray *poolStack = dic[YYNSThreadAutoleasePoolStackKey];
  47. [poolStack removeLastObject]; // pop
  48. }
  49. static void YYRunLoopAutoreleasePoolObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
  50. switch (activity) {
  51. case kCFRunLoopEntry: {
  52. YYAutoreleasePoolPush();
  53. } break;
  54. case kCFRunLoopBeforeWaiting: {
  55. YYAutoreleasePoolPop();
  56. YYAutoreleasePoolPush();
  57. } break;
  58. case kCFRunLoopExit: {
  59. YYAutoreleasePoolPop();
  60. } break;
  61. default: break;
  62. }
  63. }
  64. static void YYRunloopAutoreleasePoolSetup() {
  65. CFRunLoopRef runloop = CFRunLoopGetCurrent();
  66. CFRunLoopObserverRef pushObserver;
  67. pushObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopEntry,
  68. true, // repeat
  69. -0x7FFFFFFF, // before other observers
  70. YYRunLoopAutoreleasePoolObserverCallBack, NULL);
  71. CFRunLoopAddObserver(runloop, pushObserver, kCFRunLoopCommonModes);
  72. CFRelease(pushObserver);
  73. CFRunLoopObserverRef popObserver;
  74. popObserver = CFRunLoopObserverCreate(CFAllocatorGetDefault(), kCFRunLoopBeforeWaiting | kCFRunLoopExit,
  75. true, // repeat
  76. 0x7FFFFFFF, // after other observers
  77. YYRunLoopAutoreleasePoolObserverCallBack, NULL);
  78. CFRunLoopAddObserver(runloop, popObserver, kCFRunLoopCommonModes);
  79. CFRelease(popObserver);
  80. }
  81. @implementation NSThread (YYAdd)
  82. + (void)addAutoreleasePoolToCurrentRunloop {
  83. if ([NSThread isMainThread]) return; // The main thread already has autorelease pool.
  84. NSThread *thread = [self currentThread];
  85. if (!thread) return;
  86. if (thread.threadDictionary[YYNSThreadAutoleasePoolKey]) return; // already added
  87. YYRunloopAutoreleasePoolSetup();
  88. thread.threadDictionary[YYNSThreadAutoleasePoolKey] = YYNSThreadAutoleasePoolKey; // mark the state
  89. }
  90. @end