├── .gitignore ├── .travis.yml ├── Framework ├── Info.plist └── YYAsyncLayer.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ └── contents.xcworkspacedata │ └── xcshareddata │ └── xcschemes │ └── YYAsyncLayer.xcscheme ├── LICENSE ├── README.md ├── YYAsyncLayer.podspec └── YYAsyncLayer ├── YYAsyncLayer.h ├── YYAsyncLayer.m ├── YYSentinel.h ├── YYSentinel.m ├── YYTransaction.h └── YYTransaction.m /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcuserstate 27 | *.xcscmblueprint 28 | 29 | ## Obj-C/Swift specific 30 | *.hmap 31 | *.ipa 32 | *.dSYM.zip 33 | *.dSYM 34 | 35 | # CocoaPods 36 | # 37 | # We recommend against adding the Pods directory to your .gitignore. However 38 | # you should judge for yourself, the pros and cons are mentioned at: 39 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 40 | # 41 | # Pods/ 42 | 43 | # Carthage 44 | # 45 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 46 | # Carthage/Checkouts 47 | 48 | Carthage/Build 49 | 50 | # fastlane 51 | # 52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 53 | # screenshots whenever they are needed. 54 | # For more information about the recommended setup visit: 55 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 56 | 57 | fastlane/report.xml 58 | fastlane/Preview.html 59 | fastlane/screenshots 60 | fastlane/test_output 61 | 62 | # Code Injection 63 | # 64 | # After new code Injection tools there's a generated folder /iOSInjectionProject 65 | # https://github.com/johnno1962/injectionforxcode 66 | 67 | iOSInjectionProject/ 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode8 3 | xcode_project: Framework/YYAsyncLayer.xcodeproj 4 | xcode_scheme: YYAsyncLayer 5 | 6 | before_install: 7 | - env 8 | - xcodebuild -version 9 | - xcodebuild -showsdks 10 | - xcpretty --version 11 | 12 | script: 13 | - set -o pipefail 14 | - xcodebuild clean build -project "$TRAVIS_XCODE_PROJECT" -scheme "$TRAVIS_XCODE_SCHEME" | xcpretty 15 | -------------------------------------------------------------------------------- /Framework/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.1 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Framework/YYAsyncLayer.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D9767DFD1F04B1B100288559 /* YYAsyncLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = D9767DF71F04B1B100288559 /* YYAsyncLayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | D9767DFE1F04B1B100288559 /* YYAsyncLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = D9767DF81F04B1B100288559 /* YYAsyncLayer.m */; }; 12 | D9767DFF1F04B1B100288559 /* YYSentinel.h in Headers */ = {isa = PBXBuildFile; fileRef = D9767DF91F04B1B100288559 /* YYSentinel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | D9767E001F04B1B100288559 /* YYSentinel.m in Sources */ = {isa = PBXBuildFile; fileRef = D9767DFA1F04B1B100288559 /* YYSentinel.m */; }; 14 | D9767E011F04B1B100288559 /* YYTransaction.h in Headers */ = {isa = PBXBuildFile; fileRef = D9767DFB1F04B1B100288559 /* YYTransaction.h */; settings = {ATTRIBUTES = (Public, ); }; }; 15 | D9767E021F04B1B100288559 /* YYTransaction.m in Sources */ = {isa = PBXBuildFile; fileRef = D9767DFC1F04B1B100288559 /* YYTransaction.m */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | D9767DEB1F04B17000288559 /* YYAsyncLayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = YYAsyncLayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | D9767DEF1F04B17000288559 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Framework/Info.plist; sourceTree = ""; }; 21 | D9767DF71F04B1B100288559 /* YYAsyncLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYAsyncLayer.h; sourceTree = ""; }; 22 | D9767DF81F04B1B100288559 /* YYAsyncLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYAsyncLayer.m; sourceTree = ""; }; 23 | D9767DF91F04B1B100288559 /* YYSentinel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYSentinel.h; sourceTree = ""; }; 24 | D9767DFA1F04B1B100288559 /* YYSentinel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYSentinel.m; sourceTree = ""; }; 25 | D9767DFB1F04B1B100288559 /* YYTransaction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYTransaction.h; sourceTree = ""; }; 26 | D9767DFC1F04B1B100288559 /* YYTransaction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYTransaction.m; sourceTree = ""; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | D9767DE71F04B17000288559 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | D9767DE11F04B17000288559 = { 41 | isa = PBXGroup; 42 | children = ( 43 | D9767DF61F04B1B100288559 /* YYAsyncLayer */, 44 | D9767DEC1F04B17000288559 /* Products */, 45 | ); 46 | sourceTree = ""; 47 | }; 48 | D9767DEC1F04B17000288559 /* Products */ = { 49 | isa = PBXGroup; 50 | children = ( 51 | D9767DEB1F04B17000288559 /* YYAsyncLayer.framework */, 52 | ); 53 | name = Products; 54 | sourceTree = ""; 55 | }; 56 | D9767DF61F04B1B100288559 /* YYAsyncLayer */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | D9767DF71F04B1B100288559 /* YYAsyncLayer.h */, 60 | D9767DF81F04B1B100288559 /* YYAsyncLayer.m */, 61 | D9767DF91F04B1B100288559 /* YYSentinel.h */, 62 | D9767DFA1F04B1B100288559 /* YYSentinel.m */, 63 | D9767DFB1F04B1B100288559 /* YYTransaction.h */, 64 | D9767DFC1F04B1B100288559 /* YYTransaction.m */, 65 | D9767DEF1F04B17000288559 /* Info.plist */, 66 | ); 67 | name = YYAsyncLayer; 68 | path = ../YYAsyncLayer; 69 | sourceTree = ""; 70 | }; 71 | /* End PBXGroup section */ 72 | 73 | /* Begin PBXHeadersBuildPhase section */ 74 | D9767DE81F04B17000288559 /* Headers */ = { 75 | isa = PBXHeadersBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | D9767DFF1F04B1B100288559 /* YYSentinel.h in Headers */, 79 | D9767DFD1F04B1B100288559 /* YYAsyncLayer.h in Headers */, 80 | D9767E011F04B1B100288559 /* YYTransaction.h in Headers */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | /* End PBXHeadersBuildPhase section */ 85 | 86 | /* Begin PBXNativeTarget section */ 87 | D9767DEA1F04B17000288559 /* YYAsyncLayer */ = { 88 | isa = PBXNativeTarget; 89 | buildConfigurationList = D9767DF31F04B17000288559 /* Build configuration list for PBXNativeTarget "YYAsyncLayer" */; 90 | buildPhases = ( 91 | D9767DE61F04B17000288559 /* Sources */, 92 | D9767DE71F04B17000288559 /* Frameworks */, 93 | D9767DE81F04B17000288559 /* Headers */, 94 | D9767DE91F04B17000288559 /* Resources */, 95 | ); 96 | buildRules = ( 97 | ); 98 | dependencies = ( 99 | ); 100 | name = YYAsyncLayer; 101 | productName = YYAsyncLayer; 102 | productReference = D9767DEB1F04B17000288559 /* YYAsyncLayer.framework */; 103 | productType = "com.apple.product-type.framework"; 104 | }; 105 | /* End PBXNativeTarget section */ 106 | 107 | /* Begin PBXProject section */ 108 | D9767DE21F04B17000288559 /* Project object */ = { 109 | isa = PBXProject; 110 | attributes = { 111 | LastUpgradeCheck = 0900; 112 | ORGANIZATIONNAME = ibireme; 113 | TargetAttributes = { 114 | D9767DEA1F04B17000288559 = { 115 | CreatedOnToolsVersion = 9.0; 116 | }; 117 | }; 118 | }; 119 | buildConfigurationList = D9767DE51F04B17000288559 /* Build configuration list for PBXProject "YYAsyncLayer" */; 120 | compatibilityVersion = "Xcode 8.0"; 121 | developmentRegion = en; 122 | hasScannedForEncodings = 0; 123 | knownRegions = ( 124 | en, 125 | ); 126 | mainGroup = D9767DE11F04B17000288559; 127 | productRefGroup = D9767DEC1F04B17000288559 /* Products */; 128 | projectDirPath = ""; 129 | projectRoot = ""; 130 | targets = ( 131 | D9767DEA1F04B17000288559 /* YYAsyncLayer */, 132 | ); 133 | }; 134 | /* End PBXProject section */ 135 | 136 | /* Begin PBXResourcesBuildPhase section */ 137 | D9767DE91F04B17000288559 /* Resources */ = { 138 | isa = PBXResourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | /* End PBXResourcesBuildPhase section */ 145 | 146 | /* Begin PBXSourcesBuildPhase section */ 147 | D9767DE61F04B17000288559 /* Sources */ = { 148 | isa = PBXSourcesBuildPhase; 149 | buildActionMask = 2147483647; 150 | files = ( 151 | D9767DFE1F04B1B100288559 /* YYAsyncLayer.m in Sources */, 152 | D9767E001F04B1B100288559 /* YYSentinel.m in Sources */, 153 | D9767E021F04B1B100288559 /* YYTransaction.m in Sources */, 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | /* End PBXSourcesBuildPhase section */ 158 | 159 | /* Begin XCBuildConfiguration section */ 160 | D9767DF11F04B17000288559 /* Debug */ = { 161 | isa = XCBuildConfiguration; 162 | buildSettings = { 163 | ALWAYS_SEARCH_USER_PATHS = NO; 164 | CLANG_ANALYZER_NONNULL = YES; 165 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 166 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 167 | CLANG_CXX_LIBRARY = "libc++"; 168 | CLANG_ENABLE_MODULES = YES; 169 | CLANG_ENABLE_OBJC_ARC = YES; 170 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 171 | CLANG_WARN_BOOL_CONVERSION = YES; 172 | CLANG_WARN_COMMA = YES; 173 | CLANG_WARN_CONSTANT_CONVERSION = YES; 174 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 175 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 176 | CLANG_WARN_EMPTY_BODY = YES; 177 | CLANG_WARN_ENUM_CONVERSION = YES; 178 | CLANG_WARN_INFINITE_RECURSION = YES; 179 | CLANG_WARN_INT_CONVERSION = YES; 180 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 181 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 182 | CLANG_WARN_STRICT_PROTOTYPES = YES; 183 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 184 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 185 | CLANG_WARN_UNREACHABLE_CODE = YES; 186 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 187 | CODE_SIGN_IDENTITY = "iPhone Developer"; 188 | COPY_PHASE_STRIP = NO; 189 | CURRENT_PROJECT_VERSION = 1; 190 | DEBUG_INFORMATION_FORMAT = dwarf; 191 | ENABLE_STRICT_OBJC_MSGSEND = YES; 192 | ENABLE_TESTABILITY = YES; 193 | GCC_C_LANGUAGE_STANDARD = gnu11; 194 | GCC_DYNAMIC_NO_PIC = NO; 195 | GCC_NO_COMMON_BLOCKS = YES; 196 | GCC_OPTIMIZATION_LEVEL = 0; 197 | GCC_PREPROCESSOR_DEFINITIONS = ( 198 | "DEBUG=1", 199 | "$(inherited)", 200 | ); 201 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 202 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 203 | GCC_WARN_UNDECLARED_SELECTOR = YES; 204 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 205 | GCC_WARN_UNUSED_FUNCTION = YES; 206 | GCC_WARN_UNUSED_VARIABLE = YES; 207 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 208 | MTL_ENABLE_DEBUG_INFO = YES; 209 | ONLY_ACTIVE_ARCH = YES; 210 | SDKROOT = iphoneos; 211 | VERSIONING_SYSTEM = "apple-generic"; 212 | VERSION_INFO_PREFIX = ""; 213 | }; 214 | name = Debug; 215 | }; 216 | D9767DF21F04B17000288559 /* Release */ = { 217 | isa = XCBuildConfiguration; 218 | buildSettings = { 219 | ALWAYS_SEARCH_USER_PATHS = NO; 220 | CLANG_ANALYZER_NONNULL = YES; 221 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 227 | CLANG_WARN_BOOL_CONVERSION = YES; 228 | CLANG_WARN_COMMA = YES; 229 | CLANG_WARN_CONSTANT_CONVERSION = YES; 230 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 231 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 232 | CLANG_WARN_EMPTY_BODY = YES; 233 | CLANG_WARN_ENUM_CONVERSION = YES; 234 | CLANG_WARN_INFINITE_RECURSION = YES; 235 | CLANG_WARN_INT_CONVERSION = YES; 236 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 238 | CLANG_WARN_STRICT_PROTOTYPES = YES; 239 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 240 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 241 | CLANG_WARN_UNREACHABLE_CODE = YES; 242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 243 | CODE_SIGN_IDENTITY = "iPhone Developer"; 244 | COPY_PHASE_STRIP = NO; 245 | CURRENT_PROJECT_VERSION = 1; 246 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 247 | ENABLE_NS_ASSERTIONS = NO; 248 | ENABLE_STRICT_OBJC_MSGSEND = YES; 249 | GCC_C_LANGUAGE_STANDARD = gnu11; 250 | GCC_NO_COMMON_BLOCKS = YES; 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 258 | MTL_ENABLE_DEBUG_INFO = NO; 259 | SDKROOT = iphoneos; 260 | VALIDATE_PRODUCT = YES; 261 | VERSIONING_SYSTEM = "apple-generic"; 262 | VERSION_INFO_PREFIX = ""; 263 | }; 264 | name = Release; 265 | }; 266 | D9767DF41F04B17000288559 /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | APPLICATION_EXTENSION_API_ONLY = YES; 270 | CODE_SIGN_IDENTITY = ""; 271 | DEFINES_MODULE = YES; 272 | DYLIB_COMPATIBILITY_VERSION = 1; 273 | DYLIB_CURRENT_VERSION = 1; 274 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 275 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 276 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 277 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 278 | PRODUCT_BUNDLE_IDENTIFIER = com.ibireme.YYAsyncLayer; 279 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 280 | SKIP_INSTALL = YES; 281 | TARGETED_DEVICE_FAMILY = "1,2"; 282 | }; 283 | name = Debug; 284 | }; 285 | D9767DF51F04B17000288559 /* Release */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | APPLICATION_EXTENSION_API_ONLY = YES; 289 | CODE_SIGN_IDENTITY = ""; 290 | DEFINES_MODULE = YES; 291 | DYLIB_COMPATIBILITY_VERSION = 1; 292 | DYLIB_CURRENT_VERSION = 1; 293 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 294 | INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; 295 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 296 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 297 | PRODUCT_BUNDLE_IDENTIFIER = com.ibireme.YYAsyncLayer; 298 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 299 | SKIP_INSTALL = YES; 300 | TARGETED_DEVICE_FAMILY = "1,2"; 301 | }; 302 | name = Release; 303 | }; 304 | /* End XCBuildConfiguration section */ 305 | 306 | /* Begin XCConfigurationList section */ 307 | D9767DE51F04B17000288559 /* Build configuration list for PBXProject "YYAsyncLayer" */ = { 308 | isa = XCConfigurationList; 309 | buildConfigurations = ( 310 | D9767DF11F04B17000288559 /* Debug */, 311 | D9767DF21F04B17000288559 /* Release */, 312 | ); 313 | defaultConfigurationIsVisible = 0; 314 | defaultConfigurationName = Release; 315 | }; 316 | D9767DF31F04B17000288559 /* Build configuration list for PBXNativeTarget "YYAsyncLayer" */ = { 317 | isa = XCConfigurationList; 318 | buildConfigurations = ( 319 | D9767DF41F04B17000288559 /* Debug */, 320 | D9767DF51F04B17000288559 /* Release */, 321 | ); 322 | defaultConfigurationIsVisible = 0; 323 | defaultConfigurationName = Release; 324 | }; 325 | /* End XCConfigurationList section */ 326 | }; 327 | rootObject = D9767DE21F04B17000288559 /* Project object */; 328 | } 329 | -------------------------------------------------------------------------------- /Framework/YYAsyncLayer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Framework/YYAsyncLayer.xcodeproj/xcshareddata/xcschemes/YYAsyncLayer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ibireme 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | YYAsyncLayer 2 | ============== 3 | 4 | [![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYAsyncLayer/master/LICENSE)  5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)  6 | [![CocoaPods](http://img.shields.io/cocoapods/v/YYAsyncLayer.svg?style=flat)](http://cocoapods.org/pods/YYAsyncLayer)  7 | [![CocoaPods](http://img.shields.io/cocoapods/p/YYAsyncLayer.svg?style=flat)](http://cocoadocs.org/docsets/YYAsyncLayer)  8 | [![Support](https://img.shields.io/badge/support-iOS%206%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)  9 | [![Build Status](https://travis-ci.org/ibireme/YYAsyncLayer.svg?branch=master)](https://travis-ci.org/ibireme/YYAsyncLayer) 10 | 11 | iOS utility classes for asynchronous rendering and display.
12 | (It was used by [YYText](https://github.com/ibireme/YYText)) 13 | 14 | 15 | Simple Usage 16 | ============== 17 | 18 | ```objc 19 | @interface YYLabel : UIView 20 | @property NSString *text; 21 | @property UIFont *font; 22 | @end 23 | 24 | @implementation YYLabel 25 | 26 | - (void)setText:(NSString *)text { 27 | _text = text.copy; 28 | [[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit]; 29 | } 30 | 31 | - (void)setFont:(UIFont *)font { 32 | _font = font; 33 | [[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit]; 34 | } 35 | 36 | - (void)layoutSubviews { 37 | [super layoutSubviews]; 38 | [[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit]; 39 | } 40 | 41 | - (void)contentsNeedUpdated { 42 | // do update 43 | [self.layer setNeedsDisplay]; 44 | } 45 | 46 | #pragma mark - YYAsyncLayer 47 | 48 | + (Class)layerClass { 49 | return YYAsyncLayer.class; 50 | } 51 | 52 | - (YYAsyncLayerDisplayTask *)newAsyncDisplayTask { 53 | 54 | // capture current state to display task 55 | NSString *text = _text; 56 | UIFont *font = _font; 57 | 58 | YYAsyncLayerDisplayTask *task = [YYAsyncLayerDisplayTask new]; 59 | task.willDisplay = ^(CALayer *layer) { 60 | //... 61 | }; 62 | 63 | task.display = ^(CGContextRef context, CGSize size, BOOL(^isCancelled)(void)) { 64 | if (isCancelled()) return; 65 | NSArray *lines = CreateCTLines(text, font, size.width); 66 | if (isCancelled()) return; 67 | 68 | for (int i = 0; i < lines.count; i++) { 69 | CTLineRef line = line[i]; 70 | CGContextSetTextPosition(context, 0, i * font.pointSize * 1.5); 71 | CTLineDraw(line, context); 72 | if (isCancelled()) return; 73 | } 74 | }; 75 | 76 | task.didDisplay = ^(CALayer *layer, BOOL finished) { 77 | if (finished) { 78 | // finished 79 | } else { 80 | // cancelled 81 | } 82 | }; 83 | 84 | return task; 85 | } 86 | @end 87 | ``` 88 | 89 | Installation 90 | ============== 91 | 92 | ### CocoaPods 93 | 94 | 1. Add `pod 'YYAsyncLayer'` to your Podfile. 95 | 2. Run `pod install` or `pod update`. 96 | 3. Import \. 97 | 98 | 99 | ### Carthage 100 | 101 | 1. Add `github "ibireme/YYAsyncLayer"` to your Cartfile. 102 | 2. Run `carthage update --platform ios` and add the framework to your project. 103 | 3. Import \. 104 | 105 | 106 | ### Manually 107 | 108 | 1. Download all the files in the YYAsyncLayer subdirectory. 109 | 2. Add the source files to your Xcode project. 110 | 3. Import `YYAsyncLayer.h`. 111 | 112 | 113 | Documentation 114 | ============== 115 | Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYAsyncLayer/).
116 | You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc). 117 | 118 | 119 | Requirements 120 | ============== 121 | This library requires `iOS 6.0+` and `Xcode 8.0+`. 122 | 123 | 124 | License 125 | ============== 126 | YYAsyncLayer is provided under the MIT license. See LICENSE file for details. 127 | 128 | 129 | 130 | 131 |

132 | --- 133 | 中文介绍 134 | ============== 135 | iOS 异步绘制与显示的工具类。
136 | (该工具是从 [YYText](https://github.com/ibireme/YYText) 提取出来的独立组件) 137 | 138 | 139 | 简单用法 140 | ============== 141 | 142 | ```objc 143 | @interface YYLabel : UIView 144 | @property NSString *text; 145 | @property UIFont *font; 146 | @end 147 | 148 | @implementation YYLabel 149 | 150 | - (void)setText:(NSString *)text { 151 | _text = text.copy; 152 | [[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit]; 153 | } 154 | 155 | - (void)setFont:(UIFont *)font { 156 | _font = font; 157 | [[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit]; 158 | } 159 | 160 | - (void)layoutSubviews { 161 | [super layoutSubviews]; 162 | [[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit]; 163 | } 164 | 165 | - (void)contentsNeedUpdated { 166 | // do update 167 | [self.layer setNeedsDisplay]; 168 | } 169 | 170 | #pragma mark - YYAsyncLayer 171 | 172 | + (Class)layerClass { 173 | return YYAsyncLayer.class; 174 | } 175 | 176 | - (YYAsyncLayerDisplayTask *)newAsyncDisplayTask { 177 | 178 | // capture current state to display task 179 | NSString *text = _text; 180 | UIFont *font = _font; 181 | 182 | YYAsyncLayerDisplayTask *task = [YYAsyncLayerDisplayTask new]; 183 | task.willDisplay = ^(CALayer *layer) { 184 | //... 185 | }; 186 | 187 | task.display = ^(CGContextRef context, CGSize size, BOOL(^isCancelled)(void)) { 188 | if (isCancelled()) return; 189 | NSArray *lines = CreateCTLines(text, font, size.width); 190 | if (isCancelled()) return; 191 | 192 | for (int i = 0; i < lines.count; i++) { 193 | CTLineRef line = line[i]; 194 | CGContextSetTextPosition(context, 0, i * font.pointSize * 1.5); 195 | CTLineDraw(line, context); 196 | if (isCancelled()) return; 197 | } 198 | }; 199 | 200 | task.didDisplay = ^(CALayer *layer, BOOL finished) { 201 | if (finished) { 202 | // finished 203 | } else { 204 | // cancelled 205 | } 206 | }; 207 | 208 | return task; 209 | } 210 | @end 211 | ``` 212 | 213 | 安装 214 | ============== 215 | 216 | ### CocoaPods 217 | 218 | 1. 在 Podfile 中添加 `pod 'YYAsyncLayer'`。 219 | 2. 执行 `pod install` 或 `pod update`。 220 | 3. 导入 \。 221 | 222 | 223 | ### Carthage 224 | 225 | 1. 在 Cartfile 中添加 `github "ibireme/YYAsyncLayer"`。 226 | 2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。 227 | 3. 导入 \。 228 | 229 | 230 | ### 手动安装 231 | 232 | 1. 下载 YYAsyncLayer 文件夹内的所有内容。 233 | 2. 将 YYAsyncLayer 内的源文件添加(拖放)到你的工程。 234 | 3. 导入 `YYAsyncLayer.h`。 235 | 236 | 237 | 文档 238 | ============== 239 | 你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYAsyncLayer/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。 240 | 241 | 242 | 系统要求 243 | ============== 244 | 该项目最低支持 `iOS 6.0` 和 `Xcode 8.0`。 245 | 246 | 247 | 许可证 248 | ============== 249 | YYAsyncLayer 使用 MIT 许可证,详情见 LICENSE 文件。 250 | 251 | 相关文章 252 | ============== 253 | [iOS 保持界面流畅的技巧 254 | ](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/) 255 | 256 | -------------------------------------------------------------------------------- /YYAsyncLayer.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'YYAsyncLayer' 3 | s.summary = 'iOS utility classes for asynchronous rendering and display.' 4 | s.version = '1.0.1' 5 | s.license = { :type => 'MIT', :file => 'LICENSE' } 6 | s.authors = { 'ibireme' => 'ibireme@gmail.com' } 7 | s.social_media_url = 'http://blog.ibireme.com' 8 | s.homepage = 'https://github.com/ibireme/YYAsyncLayer' 9 | s.platform = :ios, '6.0' 10 | s.ios.deployment_target = '6.0' 11 | s.source = { :git => 'https://github.com/ibireme/YYAsyncLayer.git', :tag => s.version.to_s } 12 | 13 | s.requires_arc = true 14 | s.source_files = 'YYAsyncLayer/*.{h,m}' 15 | s.public_header_files = 'YYAsyncLayer/*.{h}' 16 | 17 | s.frameworks = 'UIKit', 'QuartzCore' 18 | 19 | end 20 | -------------------------------------------------------------------------------- /YYAsyncLayer/YYAsyncLayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYAsyncLayer.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/11. 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 | 12 | #import 13 | #import 14 | 15 | #if __has_include() 16 | FOUNDATION_EXPORT double YYAsyncLayerVersionNumber; 17 | FOUNDATION_EXPORT const unsigned char YYAsyncLayerVersionString[]; 18 | #import 19 | #import 20 | #else 21 | #import "YYSentinel.h" 22 | #import "YYTransaction.h" 23 | #endif 24 | 25 | @class YYAsyncLayerDisplayTask; 26 | 27 | NS_ASSUME_NONNULL_BEGIN 28 | 29 | /** 30 | The YYAsyncLayer class is a subclass of CALayer used for render contents asynchronously. 31 | 32 | @discussion When the layer need update it's contents, it will ask the delegate 33 | for a async display task to render the contents in a background queue. 34 | */ 35 | @interface YYAsyncLayer : CALayer 36 | /// Whether the render code is executed in background. Default is YES. 37 | @property BOOL displaysAsynchronously; 38 | @end 39 | 40 | 41 | /** 42 | The YYAsyncLayer's delegate protocol. The delegate of the YYAsyncLayer (typically a UIView) 43 | must implements the method in this protocol. 44 | */ 45 | @protocol YYAsyncLayerDelegate 46 | @required 47 | /// This method is called to return a new display task when the layer's contents need update. 48 | - (YYAsyncLayerDisplayTask *)newAsyncDisplayTask; 49 | @end 50 | 51 | 52 | /** 53 | A display task used by YYAsyncLayer to render the contents in background queue. 54 | */ 55 | @interface YYAsyncLayerDisplayTask : NSObject 56 | 57 | /** 58 | This block will be called before the asynchronous drawing begins. 59 | It will be called on the main thread. 60 | 61 | block param layer: The layer. 62 | */ 63 | @property (nullable, nonatomic, copy) void (^willDisplay)(CALayer *layer); 64 | 65 | /** 66 | This block is called to draw the layer's contents. 67 | 68 | @discussion This block may be called on main thread or background thread, 69 | so is should be thread-safe. 70 | 71 | block param context: A new bitmap content created by layer. 72 | block param size: The content size (typically same as layer's bound size). 73 | block param isCancelled: If this block returns `YES`, the method should cancel the 74 | drawing process and return as quickly as possible. 75 | */ 76 | @property (nullable, nonatomic, copy) void (^display)(CGContextRef context, CGSize size, BOOL(^isCancelled)(void)); 77 | 78 | /** 79 | This block will be called after the asynchronous drawing finished. 80 | It will be called on the main thread. 81 | 82 | block param layer: The layer. 83 | block param finished: If the draw process is cancelled, it's `NO`, otherwise it's `YES`. 84 | */ 85 | @property (nullable, nonatomic, copy) void (^didDisplay)(CALayer *layer, BOOL finished); 86 | 87 | @end 88 | 89 | NS_ASSUME_NONNULL_END 90 | -------------------------------------------------------------------------------- /YYAsyncLayer/YYAsyncLayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYAsyncLayer.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/11. 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 | 12 | #import "YYAsyncLayer.h" 13 | #import "YYSentinel.h" 14 | 15 | #if __has_include("YYDispatchQueuePool.h") 16 | #import "YYDispatchQueuePool.h" 17 | #else 18 | #import 19 | #endif 20 | 21 | /// Global display queue, used for content rendering. 22 | static dispatch_queue_t YYAsyncLayerGetDisplayQueue() { 23 | #ifdef YYDispatchQueuePool_h 24 | return YYDispatchQueueGetForQOS(NSQualityOfServiceUserInitiated); 25 | #else 26 | #define MAX_QUEUE_COUNT 16 27 | static int queueCount; 28 | static dispatch_queue_t queues[MAX_QUEUE_COUNT]; 29 | static dispatch_once_t onceToken; 30 | static int32_t counter = 0; 31 | dispatch_once(&onceToken, ^{ 32 | queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount; 33 | queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount; 34 | if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) { 35 | for (NSUInteger i = 0; i < queueCount; i++) { 36 | dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0); 37 | queues[i] = dispatch_queue_create("com.ibireme.yykit.render", attr); 38 | } 39 | } else { 40 | for (NSUInteger i = 0; i < queueCount; i++) { 41 | queues[i] = dispatch_queue_create("com.ibireme.yykit.render", DISPATCH_QUEUE_SERIAL); 42 | dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 43 | } 44 | } 45 | }); 46 | int32_t cur = OSAtomicIncrement32(&counter); 47 | if (cur < 0) cur = -cur; 48 | return queues[(cur) % queueCount]; 49 | #undef MAX_QUEUE_COUNT 50 | #endif 51 | } 52 | 53 | static dispatch_queue_t YYAsyncLayerGetReleaseQueue() { 54 | #ifdef YYDispatchQueuePool_h 55 | return YYDispatchQueueGetForQOS(NSQualityOfServiceDefault); 56 | #else 57 | return dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); 58 | #endif 59 | } 60 | 61 | 62 | @implementation YYAsyncLayerDisplayTask 63 | @end 64 | 65 | 66 | @implementation YYAsyncLayer { 67 | YYSentinel *_sentinel; 68 | } 69 | 70 | #pragma mark - Override 71 | 72 | + (id)defaultValueForKey:(NSString *)key { 73 | if ([key isEqualToString:@"displaysAsynchronously"]) { 74 | return @(YES); 75 | } else { 76 | return [super defaultValueForKey:key]; 77 | } 78 | } 79 | 80 | - (instancetype)init { 81 | self = [super init]; 82 | static CGFloat scale; //global 83 | static dispatch_once_t onceToken; 84 | dispatch_once(&onceToken, ^{ 85 | scale = [UIScreen mainScreen].scale; 86 | }); 87 | self.contentsScale = scale; 88 | _sentinel = [YYSentinel new]; 89 | _displaysAsynchronously = YES; 90 | return self; 91 | } 92 | 93 | - (void)dealloc { 94 | [_sentinel increase]; 95 | } 96 | 97 | - (void)setNeedsDisplay { 98 | [self _cancelAsyncDisplay]; 99 | [super setNeedsDisplay]; 100 | } 101 | 102 | - (void)display { 103 | super.contents = super.contents; 104 | [self _displayAsync:_displaysAsynchronously]; 105 | } 106 | 107 | #pragma mark - Private 108 | 109 | - (void)_displayAsync:(BOOL)async { 110 | __strong id delegate = (id)self.delegate; 111 | YYAsyncLayerDisplayTask *task = [delegate newAsyncDisplayTask]; 112 | if (!task.display) { 113 | if (task.willDisplay) task.willDisplay(self); 114 | self.contents = nil; 115 | if (task.didDisplay) task.didDisplay(self, YES); 116 | return; 117 | } 118 | 119 | if (async) { 120 | if (task.willDisplay) task.willDisplay(self); 121 | YYSentinel *sentinel = _sentinel; 122 | int32_t value = sentinel.value; 123 | BOOL (^isCancelled)(void) = ^BOOL() { 124 | return value != sentinel.value; 125 | }; 126 | CGSize size = self.bounds.size; 127 | BOOL opaque = self.opaque; 128 | CGFloat scale = self.contentsScale; 129 | CGColorRef backgroundColor = (opaque && self.backgroundColor) ? CGColorRetain(self.backgroundColor) : NULL; 130 | if (size.width < 1 || size.height < 1) { 131 | CGImageRef image = (__bridge_retained CGImageRef)(self.contents); 132 | self.contents = nil; 133 | if (image) { 134 | dispatch_async(YYAsyncLayerGetReleaseQueue(), ^{ 135 | CFRelease(image); 136 | }); 137 | } 138 | if (task.didDisplay) task.didDisplay(self, YES); 139 | CGColorRelease(backgroundColor); 140 | return; 141 | } 142 | 143 | dispatch_async(YYAsyncLayerGetDisplayQueue(), ^{ 144 | if (isCancelled()) { 145 | CGColorRelease(backgroundColor); 146 | return; 147 | } 148 | UIGraphicsBeginImageContextWithOptions(size, opaque, scale); 149 | CGContextRef context = UIGraphicsGetCurrentContext(); 150 | if (opaque) { 151 | CGContextSaveGState(context); { 152 | if (!backgroundColor || CGColorGetAlpha(backgroundColor) < 1) { 153 | CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); 154 | CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale)); 155 | CGContextFillPath(context); 156 | } 157 | if (backgroundColor) { 158 | CGContextSetFillColorWithColor(context, backgroundColor); 159 | CGContextAddRect(context, CGRectMake(0, 0, size.width * scale, size.height * scale)); 160 | CGContextFillPath(context); 161 | } 162 | } CGContextRestoreGState(context); 163 | CGColorRelease(backgroundColor); 164 | } 165 | task.display(context, size, isCancelled); 166 | if (isCancelled()) { 167 | UIGraphicsEndImageContext(); 168 | dispatch_async(dispatch_get_main_queue(), ^{ 169 | if (task.didDisplay) task.didDisplay(self, NO); 170 | }); 171 | return; 172 | } 173 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 174 | UIGraphicsEndImageContext(); 175 | if (isCancelled()) { 176 | dispatch_async(dispatch_get_main_queue(), ^{ 177 | if (task.didDisplay) task.didDisplay(self, NO); 178 | }); 179 | return; 180 | } 181 | dispatch_async(dispatch_get_main_queue(), ^{ 182 | if (isCancelled()) { 183 | if (task.didDisplay) task.didDisplay(self, NO); 184 | } else { 185 | self.contents = (__bridge id)(image.CGImage); 186 | if (task.didDisplay) task.didDisplay(self, YES); 187 | } 188 | }); 189 | }); 190 | } else { 191 | [_sentinel increase]; 192 | if (task.willDisplay) task.willDisplay(self); 193 | UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.opaque, self.contentsScale); 194 | CGContextRef context = UIGraphicsGetCurrentContext(); 195 | if (self.opaque) { 196 | CGSize size = self.bounds.size; 197 | size.width *= self.contentsScale; 198 | size.height *= self.contentsScale; 199 | CGContextSaveGState(context); { 200 | if (!self.backgroundColor || CGColorGetAlpha(self.backgroundColor) < 1) { 201 | CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); 202 | CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height)); 203 | CGContextFillPath(context); 204 | } 205 | if (self.backgroundColor) { 206 | CGContextSetFillColorWithColor(context, self.backgroundColor); 207 | CGContextAddRect(context, CGRectMake(0, 0, size.width, size.height)); 208 | CGContextFillPath(context); 209 | } 210 | } CGContextRestoreGState(context); 211 | } 212 | task.display(context, self.bounds.size, ^{return NO;}); 213 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 214 | UIGraphicsEndImageContext(); 215 | self.contents = (__bridge id)(image.CGImage); 216 | if (task.didDisplay) task.didDisplay(self, YES); 217 | } 218 | } 219 | 220 | - (void)_cancelAsyncDisplay { 221 | [_sentinel increase]; 222 | } 223 | 224 | @end 225 | -------------------------------------------------------------------------------- /YYAsyncLayer/YYSentinel.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYSentinel.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/13. 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 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | YYSentinel is a thread safe incrementing counter. 18 | It may be used in some multi-threaded situation. 19 | */ 20 | @interface YYSentinel : NSObject 21 | 22 | /// Returns the current value of the counter. 23 | @property (readonly) int32_t value; 24 | 25 | /// Increase the value atomically. 26 | /// @return The new value. 27 | - (int32_t)increase; 28 | 29 | @end 30 | 31 | NS_ASSUME_NONNULL_END 32 | -------------------------------------------------------------------------------- /YYAsyncLayer/YYSentinel.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYSentinel.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/13. 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 | 12 | #import "YYSentinel.h" 13 | #import 14 | 15 | @implementation YYSentinel { 16 | int32_t _value; 17 | } 18 | 19 | - (int32_t)value { 20 | return _value; 21 | } 22 | 23 | - (int32_t)increase { 24 | return OSAtomicIncrement32(&_value); 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /YYAsyncLayer/YYTransaction.h: -------------------------------------------------------------------------------- 1 | // 2 | // YYTransaction.h 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/18. 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 | 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | /** 17 | YYTransaction let you perform a selector once before current runloop sleep. 18 | */ 19 | @interface YYTransaction : NSObject 20 | 21 | /** 22 | Creates and returns a transaction with a specified target and selector. 23 | 24 | @param target A specified target, the target is retained until runloop end. 25 | @param selector A selector for target. 26 | 27 | @return A new transaction, or nil if an error occurs. 28 | */ 29 | + (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector; 30 | 31 | /** 32 | Commit the trancaction to main runloop. 33 | 34 | @discussion It will perform the selector on the target once before main runloop's 35 | current loop sleep. If the same transaction (same target and same selector) has 36 | already commit to runloop in this loop, this method do nothing. 37 | */ 38 | - (void)commit; 39 | 40 | @end 41 | 42 | NS_ASSUME_NONNULL_END 43 | -------------------------------------------------------------------------------- /YYAsyncLayer/YYTransaction.m: -------------------------------------------------------------------------------- 1 | // 2 | // YYTransaction.m 3 | // YYKit 4 | // 5 | // Created by ibireme on 15/4/18. 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 | 12 | #import "YYTransaction.h" 13 | 14 | 15 | @interface YYTransaction() 16 | @property (nonatomic, strong) id target; 17 | @property (nonatomic, assign) SEL selector; 18 | @end 19 | 20 | static NSMutableSet *transactionSet = nil; 21 | 22 | static void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { 23 | if (transactionSet.count == 0) return; 24 | NSSet *currentSet = transactionSet; 25 | transactionSet = [NSMutableSet new]; 26 | [currentSet enumerateObjectsUsingBlock:^(YYTransaction *transaction, BOOL *stop) { 27 | #pragma clang diagnostic push 28 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 29 | [transaction.target performSelector:transaction.selector]; 30 | #pragma clang diagnostic pop 31 | }]; 32 | } 33 | 34 | static void YYTransactionSetup() { 35 | static dispatch_once_t onceToken; 36 | dispatch_once(&onceToken, ^{ 37 | transactionSet = [NSMutableSet new]; 38 | CFRunLoopRef runloop = CFRunLoopGetMain(); 39 | CFRunLoopObserverRef observer; 40 | 41 | observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(), 42 | kCFRunLoopBeforeWaiting | kCFRunLoopExit, 43 | true, // repeat 44 | 0xFFFFFF, // after CATransaction(2000000) 45 | YYRunLoopObserverCallBack, NULL); 46 | CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes); 47 | CFRelease(observer); 48 | }); 49 | } 50 | 51 | 52 | @implementation YYTransaction 53 | 54 | + (YYTransaction *)transactionWithTarget:(id)target selector:(SEL)selector{ 55 | if (!target || !selector) return nil; 56 | YYTransaction *t = [YYTransaction new]; 57 | t.target = target; 58 | t.selector = selector; 59 | return t; 60 | } 61 | 62 | - (void)commit { 63 | if (!_target || !_selector) return; 64 | YYTransactionSetup(); 65 | [transactionSet addObject:self]; 66 | } 67 | 68 | - (NSUInteger)hash { 69 | long v1 = (long)((void *)_selector); 70 | long v2 = (long)_target; 71 | return v1 ^ v2; 72 | } 73 | 74 | - (BOOL)isEqual:(id)object { 75 | if (self == object) return YES; 76 | if (![object isMemberOfClass:self.class]) return NO; 77 | YYTransaction *other = object; 78 | return other.selector == _selector && other.target == _target; 79 | } 80 | 81 | @end 82 | --------------------------------------------------------------------------------