├── .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 | [](https://raw.githubusercontent.com/ibireme/YYAsyncLayer/master/LICENSE)
5 | [](https://github.com/Carthage/Carthage)
6 | [](http://cocoapods.org/pods/YYAsyncLayer)
7 | [](http://cocoadocs.org/docsets/YYAsyncLayer)
8 | [](https://www.apple.com/nl/ios/)
9 | [](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 |
--------------------------------------------------------------------------------