├── .DS_Store ├── .gitignore ├── LICENSE ├── MutipleThread ├── MutipleThread.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── MutipleThread │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── GCDLoadViewController.h │ ├── GCDLoadViewController.m │ ├── Info.plist │ ├── LoadImageOperation.h │ ├── LoadImageOperation.m │ ├── NSOperationLoadViewController.h │ ├── NSOperationLoadViewController.m │ ├── NSThreadLoadViewController.h │ ├── NSThreadLoadViewController.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── MutipleThreadTests │ ├── Info.plist │ └── MutipleThreadTests.m ├── MutipleThreadUITests │ ├── Info.plist │ └── MutipleThreadUITests.m └── Toast │ ├── UIView+Toast.h │ └── UIView+Toast.m ├── README.md └── picture ├── .DS_Store ├── multiplethread1.png ├── multiplethread2.jpeg ├── mutiplethread1.gif ├── mutiplethread2.gif └── mutiplethread3.gif /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minggo620/iOSMutipleThread/3ee36aaf69dc5b2400fbb17ffdf4da76a47c84c0/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 35 | # 36 | # Pods/ 37 | 38 | # Carthage 39 | # 40 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 41 | # Carthage/Checkouts 42 | 43 | Carthage/Build 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 51 | 52 | fastlane/report.xml 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 minggo 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 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4608E21F1C585FE3009B94F8 /* UIView+Toast.m in Sources */ = {isa = PBXBuildFile; fileRef = 4608E21E1C585FE3009B94F8 /* UIView+Toast.m */; }; 11 | 46254BBF1C55FCF9004D75F2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 46254BBE1C55FCF9004D75F2 /* main.m */; }; 12 | 46254BC21C55FCF9004D75F2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 46254BC11C55FCF9004D75F2 /* AppDelegate.m */; }; 13 | 46254BC51C55FCF9004D75F2 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 46254BC41C55FCF9004D75F2 /* ViewController.m */; }; 14 | 46254BC81C55FCF9004D75F2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46254BC61C55FCF9004D75F2 /* Main.storyboard */; }; 15 | 46254BCA1C55FCF9004D75F2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 46254BC91C55FCF9004D75F2 /* Assets.xcassets */; }; 16 | 46254BCD1C55FCF9004D75F2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 46254BCB1C55FCF9004D75F2 /* LaunchScreen.storyboard */; }; 17 | 46254BD81C55FCF9004D75F2 /* MutipleThreadTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 46254BD71C55FCF9004D75F2 /* MutipleThreadTests.m */; }; 18 | 46254BE31C55FCF9004D75F2 /* MutipleThreadUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 46254BE21C55FCF9004D75F2 /* MutipleThreadUITests.m */; }; 19 | 465D56EB1C571D82005FD699 /* NSThreadLoadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 465D56EA1C571D82005FD699 /* NSThreadLoadViewController.m */; }; 20 | 465D56EE1C571D99005FD699 /* NSOperationLoadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 465D56ED1C571D99005FD699 /* NSOperationLoadViewController.m */; }; 21 | 465D56F11C571DAA005FD699 /* GCDLoadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 465D56F01C571DAA005FD699 /* GCDLoadViewController.m */; }; 22 | 465D56F41C576835005FD699 /* LoadImageOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 465D56F31C576835005FD699 /* LoadImageOperation.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 46254BD41C55FCF9004D75F2 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 46254BB21C55FCF9004D75F2 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 46254BB91C55FCF9004D75F2; 31 | remoteInfo = MutipleThread; 32 | }; 33 | 46254BDF1C55FCF9004D75F2 /* PBXContainerItemProxy */ = { 34 | isa = PBXContainerItemProxy; 35 | containerPortal = 46254BB21C55FCF9004D75F2 /* Project object */; 36 | proxyType = 1; 37 | remoteGlobalIDString = 46254BB91C55FCF9004D75F2; 38 | remoteInfo = MutipleThread; 39 | }; 40 | /* End PBXContainerItemProxy section */ 41 | 42 | /* Begin PBXFileReference section */ 43 | 4608E21D1C585FE3009B94F8 /* UIView+Toast.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Toast.h"; sourceTree = ""; }; 44 | 4608E21E1C585FE3009B94F8 /* UIView+Toast.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Toast.m"; sourceTree = ""; }; 45 | 46254BBA1C55FCF9004D75F2 /* MutipleThread.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MutipleThread.app; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 46254BBE1C55FCF9004D75F2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | 46254BC01C55FCF9004D75F2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 46254BC11C55FCF9004D75F2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 46254BC31C55FCF9004D75F2 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 50 | 46254BC41C55FCF9004D75F2 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 51 | 46254BC71C55FCF9004D75F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 52 | 46254BC91C55FCF9004D75F2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 53 | 46254BCC1C55FCF9004D75F2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 54 | 46254BCE1C55FCF9004D75F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | 46254BD31C55FCF9004D75F2 /* MutipleThreadTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MutipleThreadTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 46254BD71C55FCF9004D75F2 /* MutipleThreadTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MutipleThreadTests.m; sourceTree = ""; }; 57 | 46254BD91C55FCF9004D75F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | 46254BDE1C55FCF9004D75F2 /* MutipleThreadUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MutipleThreadUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 46254BE21C55FCF9004D75F2 /* MutipleThreadUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MutipleThreadUITests.m; sourceTree = ""; }; 60 | 46254BE41C55FCF9004D75F2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 61 | 465D56E91C571D82005FD699 /* NSThreadLoadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSThreadLoadViewController.h; sourceTree = ""; }; 62 | 465D56EA1C571D82005FD699 /* NSThreadLoadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSThreadLoadViewController.m; sourceTree = ""; }; 63 | 465D56EC1C571D99005FD699 /* NSOperationLoadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSOperationLoadViewController.h; sourceTree = ""; }; 64 | 465D56ED1C571D99005FD699 /* NSOperationLoadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSOperationLoadViewController.m; sourceTree = ""; }; 65 | 465D56EF1C571DAA005FD699 /* GCDLoadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDLoadViewController.h; sourceTree = ""; }; 66 | 465D56F01C571DAA005FD699 /* GCDLoadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDLoadViewController.m; sourceTree = ""; }; 67 | 465D56F21C576835005FD699 /* LoadImageOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadImageOperation.h; sourceTree = ""; }; 68 | 465D56F31C576835005FD699 /* LoadImageOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoadImageOperation.m; sourceTree = ""; }; 69 | /* End PBXFileReference section */ 70 | 71 | /* Begin PBXFrameworksBuildPhase section */ 72 | 46254BB71C55FCF9004D75F2 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 46254BD01C55FCF9004D75F2 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | ); 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | 46254BDB1C55FCF9004D75F2 /* Frameworks */ = { 87 | isa = PBXFrameworksBuildPhase; 88 | buildActionMask = 2147483647; 89 | files = ( 90 | ); 91 | runOnlyForDeploymentPostprocessing = 0; 92 | }; 93 | /* End PBXFrameworksBuildPhase section */ 94 | 95 | /* Begin PBXGroup section */ 96 | 4608E2191C585E2A009B94F8 /* NSThread Load */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 465D56E91C571D82005FD699 /* NSThreadLoadViewController.h */, 100 | 465D56EA1C571D82005FD699 /* NSThreadLoadViewController.m */, 101 | ); 102 | name = "NSThread Load"; 103 | sourceTree = ""; 104 | }; 105 | 4608E21A1C585E55009B94F8 /* NSOperation Load */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 465D56EC1C571D99005FD699 /* NSOperationLoadViewController.h */, 109 | 465D56ED1C571D99005FD699 /* NSOperationLoadViewController.m */, 110 | 465D56F21C576835005FD699 /* LoadImageOperation.h */, 111 | 465D56F31C576835005FD699 /* LoadImageOperation.m */, 112 | ); 113 | name = "NSOperation Load"; 114 | sourceTree = ""; 115 | }; 116 | 4608E21B1C585EA0009B94F8 /* GCD Load */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 465D56EF1C571DAA005FD699 /* GCDLoadViewController.h */, 120 | 465D56F01C571DAA005FD699 /* GCDLoadViewController.m */, 121 | ); 122 | name = "GCD Load"; 123 | sourceTree = ""; 124 | }; 125 | 4608E21C1C585FE3009B94F8 /* Toast */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 4608E21D1C585FE3009B94F8 /* UIView+Toast.h */, 129 | 4608E21E1C585FE3009B94F8 /* UIView+Toast.m */, 130 | ); 131 | path = Toast; 132 | sourceTree = ""; 133 | }; 134 | 46254BB11C55FCF9004D75F2 = { 135 | isa = PBXGroup; 136 | children = ( 137 | 46254BBC1C55FCF9004D75F2 /* MutipleThread */, 138 | 4608E21C1C585FE3009B94F8 /* Toast */, 139 | 46254BD61C55FCF9004D75F2 /* MutipleThreadTests */, 140 | 46254BE11C55FCF9004D75F2 /* MutipleThreadUITests */, 141 | 46254BBB1C55FCF9004D75F2 /* Products */, 142 | ); 143 | sourceTree = ""; 144 | }; 145 | 46254BBB1C55FCF9004D75F2 /* Products */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 46254BBA1C55FCF9004D75F2 /* MutipleThread.app */, 149 | 46254BD31C55FCF9004D75F2 /* MutipleThreadTests.xctest */, 150 | 46254BDE1C55FCF9004D75F2 /* MutipleThreadUITests.xctest */, 151 | ); 152 | name = Products; 153 | sourceTree = ""; 154 | }; 155 | 46254BBC1C55FCF9004D75F2 /* MutipleThread */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | 4608E21B1C585EA0009B94F8 /* GCD Load */, 159 | 4608E21A1C585E55009B94F8 /* NSOperation Load */, 160 | 4608E2191C585E2A009B94F8 /* NSThread Load */, 161 | 46254BC01C55FCF9004D75F2 /* AppDelegate.h */, 162 | 46254BC11C55FCF9004D75F2 /* AppDelegate.m */, 163 | 46254BC31C55FCF9004D75F2 /* ViewController.h */, 164 | 46254BC41C55FCF9004D75F2 /* ViewController.m */, 165 | 46254BC61C55FCF9004D75F2 /* Main.storyboard */, 166 | 46254BC91C55FCF9004D75F2 /* Assets.xcassets */, 167 | 46254BCB1C55FCF9004D75F2 /* LaunchScreen.storyboard */, 168 | 46254BCE1C55FCF9004D75F2 /* Info.plist */, 169 | 46254BBD1C55FCF9004D75F2 /* Supporting Files */, 170 | ); 171 | path = MutipleThread; 172 | sourceTree = ""; 173 | }; 174 | 46254BBD1C55FCF9004D75F2 /* Supporting Files */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 46254BBE1C55FCF9004D75F2 /* main.m */, 178 | ); 179 | name = "Supporting Files"; 180 | sourceTree = ""; 181 | }; 182 | 46254BD61C55FCF9004D75F2 /* MutipleThreadTests */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 46254BD71C55FCF9004D75F2 /* MutipleThreadTests.m */, 186 | 46254BD91C55FCF9004D75F2 /* Info.plist */, 187 | ); 188 | path = MutipleThreadTests; 189 | sourceTree = ""; 190 | }; 191 | 46254BE11C55FCF9004D75F2 /* MutipleThreadUITests */ = { 192 | isa = PBXGroup; 193 | children = ( 194 | 46254BE21C55FCF9004D75F2 /* MutipleThreadUITests.m */, 195 | 46254BE41C55FCF9004D75F2 /* Info.plist */, 196 | ); 197 | path = MutipleThreadUITests; 198 | sourceTree = ""; 199 | }; 200 | /* End PBXGroup section */ 201 | 202 | /* Begin PBXNativeTarget section */ 203 | 46254BB91C55FCF9004D75F2 /* MutipleThread */ = { 204 | isa = PBXNativeTarget; 205 | buildConfigurationList = 46254BE71C55FCF9004D75F2 /* Build configuration list for PBXNativeTarget "MutipleThread" */; 206 | buildPhases = ( 207 | 46254BB61C55FCF9004D75F2 /* Sources */, 208 | 46254BB71C55FCF9004D75F2 /* Frameworks */, 209 | 46254BB81C55FCF9004D75F2 /* Resources */, 210 | ); 211 | buildRules = ( 212 | ); 213 | dependencies = ( 214 | ); 215 | name = MutipleThread; 216 | productName = MutipleThread; 217 | productReference = 46254BBA1C55FCF9004D75F2 /* MutipleThread.app */; 218 | productType = "com.apple.product-type.application"; 219 | }; 220 | 46254BD21C55FCF9004D75F2 /* MutipleThreadTests */ = { 221 | isa = PBXNativeTarget; 222 | buildConfigurationList = 46254BEA1C55FCF9004D75F2 /* Build configuration list for PBXNativeTarget "MutipleThreadTests" */; 223 | buildPhases = ( 224 | 46254BCF1C55FCF9004D75F2 /* Sources */, 225 | 46254BD01C55FCF9004D75F2 /* Frameworks */, 226 | 46254BD11C55FCF9004D75F2 /* Resources */, 227 | ); 228 | buildRules = ( 229 | ); 230 | dependencies = ( 231 | 46254BD51C55FCF9004D75F2 /* PBXTargetDependency */, 232 | ); 233 | name = MutipleThreadTests; 234 | productName = MutipleThreadTests; 235 | productReference = 46254BD31C55FCF9004D75F2 /* MutipleThreadTests.xctest */; 236 | productType = "com.apple.product-type.bundle.unit-test"; 237 | }; 238 | 46254BDD1C55FCF9004D75F2 /* MutipleThreadUITests */ = { 239 | isa = PBXNativeTarget; 240 | buildConfigurationList = 46254BED1C55FCF9004D75F2 /* Build configuration list for PBXNativeTarget "MutipleThreadUITests" */; 241 | buildPhases = ( 242 | 46254BDA1C55FCF9004D75F2 /* Sources */, 243 | 46254BDB1C55FCF9004D75F2 /* Frameworks */, 244 | 46254BDC1C55FCF9004D75F2 /* Resources */, 245 | ); 246 | buildRules = ( 247 | ); 248 | dependencies = ( 249 | 46254BE01C55FCF9004D75F2 /* PBXTargetDependency */, 250 | ); 251 | name = MutipleThreadUITests; 252 | productName = MutipleThreadUITests; 253 | productReference = 46254BDE1C55FCF9004D75F2 /* MutipleThreadUITests.xctest */; 254 | productType = "com.apple.product-type.bundle.ui-testing"; 255 | }; 256 | /* End PBXNativeTarget section */ 257 | 258 | /* Begin PBXProject section */ 259 | 46254BB21C55FCF9004D75F2 /* Project object */ = { 260 | isa = PBXProject; 261 | attributes = { 262 | LastUpgradeCheck = 0720; 263 | ORGANIZATIONNAME = minggo; 264 | TargetAttributes = { 265 | 46254BB91C55FCF9004D75F2 = { 266 | CreatedOnToolsVersion = 7.2; 267 | }; 268 | 46254BD21C55FCF9004D75F2 = { 269 | CreatedOnToolsVersion = 7.2; 270 | TestTargetID = 46254BB91C55FCF9004D75F2; 271 | }; 272 | 46254BDD1C55FCF9004D75F2 = { 273 | CreatedOnToolsVersion = 7.2; 274 | TestTargetID = 46254BB91C55FCF9004D75F2; 275 | }; 276 | }; 277 | }; 278 | buildConfigurationList = 46254BB51C55FCF9004D75F2 /* Build configuration list for PBXProject "MutipleThread" */; 279 | compatibilityVersion = "Xcode 3.2"; 280 | developmentRegion = English; 281 | hasScannedForEncodings = 0; 282 | knownRegions = ( 283 | en, 284 | Base, 285 | ); 286 | mainGroup = 46254BB11C55FCF9004D75F2; 287 | productRefGroup = 46254BBB1C55FCF9004D75F2 /* Products */; 288 | projectDirPath = ""; 289 | projectRoot = ""; 290 | targets = ( 291 | 46254BB91C55FCF9004D75F2 /* MutipleThread */, 292 | 46254BD21C55FCF9004D75F2 /* MutipleThreadTests */, 293 | 46254BDD1C55FCF9004D75F2 /* MutipleThreadUITests */, 294 | ); 295 | }; 296 | /* End PBXProject section */ 297 | 298 | /* Begin PBXResourcesBuildPhase section */ 299 | 46254BB81C55FCF9004D75F2 /* Resources */ = { 300 | isa = PBXResourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | 46254BCD1C55FCF9004D75F2 /* LaunchScreen.storyboard in Resources */, 304 | 46254BCA1C55FCF9004D75F2 /* Assets.xcassets in Resources */, 305 | 46254BC81C55FCF9004D75F2 /* Main.storyboard in Resources */, 306 | ); 307 | runOnlyForDeploymentPostprocessing = 0; 308 | }; 309 | 46254BD11C55FCF9004D75F2 /* Resources */ = { 310 | isa = PBXResourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | 46254BDC1C55FCF9004D75F2 /* Resources */ = { 317 | isa = PBXResourcesBuildPhase; 318 | buildActionMask = 2147483647; 319 | files = ( 320 | ); 321 | runOnlyForDeploymentPostprocessing = 0; 322 | }; 323 | /* End PBXResourcesBuildPhase section */ 324 | 325 | /* Begin PBXSourcesBuildPhase section */ 326 | 46254BB61C55FCF9004D75F2 /* Sources */ = { 327 | isa = PBXSourcesBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | 4608E21F1C585FE3009B94F8 /* UIView+Toast.m in Sources */, 331 | 465D56F41C576835005FD699 /* LoadImageOperation.m in Sources */, 332 | 46254BC51C55FCF9004D75F2 /* ViewController.m in Sources */, 333 | 46254BC21C55FCF9004D75F2 /* AppDelegate.m in Sources */, 334 | 465D56EE1C571D99005FD699 /* NSOperationLoadViewController.m in Sources */, 335 | 465D56F11C571DAA005FD699 /* GCDLoadViewController.m in Sources */, 336 | 46254BBF1C55FCF9004D75F2 /* main.m in Sources */, 337 | 465D56EB1C571D82005FD699 /* NSThreadLoadViewController.m in Sources */, 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | }; 341 | 46254BCF1C55FCF9004D75F2 /* Sources */ = { 342 | isa = PBXSourcesBuildPhase; 343 | buildActionMask = 2147483647; 344 | files = ( 345 | 46254BD81C55FCF9004D75F2 /* MutipleThreadTests.m in Sources */, 346 | ); 347 | runOnlyForDeploymentPostprocessing = 0; 348 | }; 349 | 46254BDA1C55FCF9004D75F2 /* Sources */ = { 350 | isa = PBXSourcesBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | 46254BE31C55FCF9004D75F2 /* MutipleThreadUITests.m in Sources */, 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | /* End PBXSourcesBuildPhase section */ 358 | 359 | /* Begin PBXTargetDependency section */ 360 | 46254BD51C55FCF9004D75F2 /* PBXTargetDependency */ = { 361 | isa = PBXTargetDependency; 362 | target = 46254BB91C55FCF9004D75F2 /* MutipleThread */; 363 | targetProxy = 46254BD41C55FCF9004D75F2 /* PBXContainerItemProxy */; 364 | }; 365 | 46254BE01C55FCF9004D75F2 /* PBXTargetDependency */ = { 366 | isa = PBXTargetDependency; 367 | target = 46254BB91C55FCF9004D75F2 /* MutipleThread */; 368 | targetProxy = 46254BDF1C55FCF9004D75F2 /* PBXContainerItemProxy */; 369 | }; 370 | /* End PBXTargetDependency section */ 371 | 372 | /* Begin PBXVariantGroup section */ 373 | 46254BC61C55FCF9004D75F2 /* Main.storyboard */ = { 374 | isa = PBXVariantGroup; 375 | children = ( 376 | 46254BC71C55FCF9004D75F2 /* Base */, 377 | ); 378 | name = Main.storyboard; 379 | sourceTree = ""; 380 | }; 381 | 46254BCB1C55FCF9004D75F2 /* LaunchScreen.storyboard */ = { 382 | isa = PBXVariantGroup; 383 | children = ( 384 | 46254BCC1C55FCF9004D75F2 /* Base */, 385 | ); 386 | name = LaunchScreen.storyboard; 387 | sourceTree = ""; 388 | }; 389 | /* End PBXVariantGroup section */ 390 | 391 | /* Begin XCBuildConfiguration section */ 392 | 46254BE51C55FCF9004D75F2 /* Debug */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | ALWAYS_SEARCH_USER_PATHS = NO; 396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 397 | CLANG_CXX_LIBRARY = "libc++"; 398 | CLANG_ENABLE_MODULES = YES; 399 | CLANG_ENABLE_OBJC_ARC = YES; 400 | CLANG_WARN_BOOL_CONVERSION = YES; 401 | CLANG_WARN_CONSTANT_CONVERSION = YES; 402 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 403 | CLANG_WARN_EMPTY_BODY = YES; 404 | CLANG_WARN_ENUM_CONVERSION = YES; 405 | CLANG_WARN_INT_CONVERSION = YES; 406 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 407 | CLANG_WARN_UNREACHABLE_CODE = YES; 408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 409 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 410 | COPY_PHASE_STRIP = NO; 411 | DEBUG_INFORMATION_FORMAT = dwarf; 412 | ENABLE_STRICT_OBJC_MSGSEND = YES; 413 | ENABLE_TESTABILITY = YES; 414 | GCC_C_LANGUAGE_STANDARD = gnu99; 415 | GCC_DYNAMIC_NO_PIC = NO; 416 | GCC_NO_COMMON_BLOCKS = YES; 417 | GCC_OPTIMIZATION_LEVEL = 0; 418 | GCC_PREPROCESSOR_DEFINITIONS = ( 419 | "DEBUG=1", 420 | "$(inherited)", 421 | ); 422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 423 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 424 | GCC_WARN_UNDECLARED_SELECTOR = YES; 425 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 426 | GCC_WARN_UNUSED_FUNCTION = YES; 427 | GCC_WARN_UNUSED_VARIABLE = YES; 428 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 429 | MTL_ENABLE_DEBUG_INFO = YES; 430 | ONLY_ACTIVE_ARCH = YES; 431 | SDKROOT = iphoneos; 432 | TARGETED_DEVICE_FAMILY = "1,2"; 433 | }; 434 | name = Debug; 435 | }; 436 | 46254BE61C55FCF9004D75F2 /* Release */ = { 437 | isa = XCBuildConfiguration; 438 | buildSettings = { 439 | ALWAYS_SEARCH_USER_PATHS = NO; 440 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 441 | CLANG_CXX_LIBRARY = "libc++"; 442 | CLANG_ENABLE_MODULES = YES; 443 | CLANG_ENABLE_OBJC_ARC = YES; 444 | CLANG_WARN_BOOL_CONVERSION = YES; 445 | CLANG_WARN_CONSTANT_CONVERSION = YES; 446 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 447 | CLANG_WARN_EMPTY_BODY = YES; 448 | CLANG_WARN_ENUM_CONVERSION = YES; 449 | CLANG_WARN_INT_CONVERSION = YES; 450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 451 | CLANG_WARN_UNREACHABLE_CODE = YES; 452 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 453 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 454 | COPY_PHASE_STRIP = NO; 455 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 456 | ENABLE_NS_ASSERTIONS = NO; 457 | ENABLE_STRICT_OBJC_MSGSEND = YES; 458 | GCC_C_LANGUAGE_STANDARD = gnu99; 459 | GCC_NO_COMMON_BLOCKS = YES; 460 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 461 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 462 | GCC_WARN_UNDECLARED_SELECTOR = YES; 463 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 464 | GCC_WARN_UNUSED_FUNCTION = YES; 465 | GCC_WARN_UNUSED_VARIABLE = YES; 466 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 467 | MTL_ENABLE_DEBUG_INFO = NO; 468 | SDKROOT = iphoneos; 469 | TARGETED_DEVICE_FAMILY = "1,2"; 470 | VALIDATE_PRODUCT = YES; 471 | }; 472 | name = Release; 473 | }; 474 | 46254BE81C55FCF9004D75F2 /* Debug */ = { 475 | isa = XCBuildConfiguration; 476 | buildSettings = { 477 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 478 | INFOPLIST_FILE = MutipleThread/Info.plist; 479 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 481 | PRODUCT_BUNDLE_IDENTIFIER = com.minggo.mutiple.thread.MutipleThread; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | }; 484 | name = Debug; 485 | }; 486 | 46254BE91C55FCF9004D75F2 /* Release */ = { 487 | isa = XCBuildConfiguration; 488 | buildSettings = { 489 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 490 | INFOPLIST_FILE = MutipleThread/Info.plist; 491 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 492 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 493 | PRODUCT_BUNDLE_IDENTIFIER = com.minggo.mutiple.thread.MutipleThread; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | }; 496 | name = Release; 497 | }; 498 | 46254BEB1C55FCF9004D75F2 /* Debug */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | BUNDLE_LOADER = "$(TEST_HOST)"; 502 | INFOPLIST_FILE = MutipleThreadTests/Info.plist; 503 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 504 | PRODUCT_BUNDLE_IDENTIFIER = com.minggo.mutiple.thread.MutipleThreadTests; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MutipleThread.app/MutipleThread"; 507 | }; 508 | name = Debug; 509 | }; 510 | 46254BEC1C55FCF9004D75F2 /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | BUNDLE_LOADER = "$(TEST_HOST)"; 514 | INFOPLIST_FILE = MutipleThreadTests/Info.plist; 515 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 516 | PRODUCT_BUNDLE_IDENTIFIER = com.minggo.mutiple.thread.MutipleThreadTests; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MutipleThread.app/MutipleThread"; 519 | }; 520 | name = Release; 521 | }; 522 | 46254BEE1C55FCF9004D75F2 /* Debug */ = { 523 | isa = XCBuildConfiguration; 524 | buildSettings = { 525 | INFOPLIST_FILE = MutipleThreadUITests/Info.plist; 526 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 527 | PRODUCT_BUNDLE_IDENTIFIER = com.minggo.mutiple.thread.MutipleThreadUITests; 528 | PRODUCT_NAME = "$(TARGET_NAME)"; 529 | TEST_TARGET_NAME = MutipleThread; 530 | USES_XCTRUNNER = YES; 531 | }; 532 | name = Debug; 533 | }; 534 | 46254BEF1C55FCF9004D75F2 /* Release */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | INFOPLIST_FILE = MutipleThreadUITests/Info.plist; 538 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 539 | PRODUCT_BUNDLE_IDENTIFIER = com.minggo.mutiple.thread.MutipleThreadUITests; 540 | PRODUCT_NAME = "$(TARGET_NAME)"; 541 | TEST_TARGET_NAME = MutipleThread; 542 | USES_XCTRUNNER = YES; 543 | }; 544 | name = Release; 545 | }; 546 | /* End XCBuildConfiguration section */ 547 | 548 | /* Begin XCConfigurationList section */ 549 | 46254BB51C55FCF9004D75F2 /* Build configuration list for PBXProject "MutipleThread" */ = { 550 | isa = XCConfigurationList; 551 | buildConfigurations = ( 552 | 46254BE51C55FCF9004D75F2 /* Debug */, 553 | 46254BE61C55FCF9004D75F2 /* Release */, 554 | ); 555 | defaultConfigurationIsVisible = 0; 556 | defaultConfigurationName = Release; 557 | }; 558 | 46254BE71C55FCF9004D75F2 /* Build configuration list for PBXNativeTarget "MutipleThread" */ = { 559 | isa = XCConfigurationList; 560 | buildConfigurations = ( 561 | 46254BE81C55FCF9004D75F2 /* Debug */, 562 | 46254BE91C55FCF9004D75F2 /* Release */, 563 | ); 564 | defaultConfigurationIsVisible = 0; 565 | defaultConfigurationName = Release; 566 | }; 567 | 46254BEA1C55FCF9004D75F2 /* Build configuration list for PBXNativeTarget "MutipleThreadTests" */ = { 568 | isa = XCConfigurationList; 569 | buildConfigurations = ( 570 | 46254BEB1C55FCF9004D75F2 /* Debug */, 571 | 46254BEC1C55FCF9004D75F2 /* Release */, 572 | ); 573 | defaultConfigurationIsVisible = 0; 574 | defaultConfigurationName = Release; 575 | }; 576 | 46254BED1C55FCF9004D75F2 /* Build configuration list for PBXNativeTarget "MutipleThreadUITests" */ = { 577 | isa = XCConfigurationList; 578 | buildConfigurations = ( 579 | 46254BEE1C55FCF9004D75F2 /* Debug */, 580 | 46254BEF1C55FCF9004D75F2 /* Release */, 581 | ); 582 | defaultConfigurationIsVisible = 0; 583 | defaultConfigurationName = Release; 584 | }; 585 | /* End XCConfigurationList section */ 586 | }; 587 | rootObject = 46254BB21C55FCF9004D75F2 /* Project object */; 588 | } 589 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/25. 6 | // Copyright © 2016年 minggo. 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 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/25. 6 | // Copyright © 2016年 minggo. 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 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 30 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 74 | 85 | 96 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 165 | 176 | 187 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 272 | 283 | 293 | 304 | 315 | 326 | 337 | 348 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/GCDLoadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // GCDLoadViewController.h 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface GCDLoadViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/GCDLoadViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // GCDLoadViewController.m 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import "GCDLoadViewController.h" 10 | #import "UIView+Toast.h" 11 | 12 | @interface GCDLoadViewController() 13 | @property (weak, nonatomic) IBOutlet UIImageView *imageview1; 14 | @property (weak, nonatomic) IBOutlet UIImageView *imageView2; 15 | @property (weak, nonatomic) IBOutlet UILabel *loadingLb; 16 | - (IBAction)onClick:(id)sender; 17 | 18 | 19 | @end 20 | 21 | @implementation GCDLoadViewController{ 22 | NSString *imgUrl1; 23 | NSString *imgUrl2; 24 | } 25 | 26 | -(void)viewDidLoad{ 27 | [super viewDidLoad]; 28 | imgUrl1 = @"https://d262ilb51hltx0.cloudfront.net/fit/t/880/264/1*zF0J7XHubBjojgJdYRS0FA.jpeg"; 29 | imgUrl2 = @"https://d262ilb51hltx0.cloudfront.net/fit/t/880/264/1*kE8-X3OjeiiSPQFyhL2Tdg.jpeg"; 30 | } 31 | 32 | //后台执行 33 | -(void)globalQueue{ 34 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 35 | [self loadImageSource:imgUrl1]; 36 | }); 37 | } 38 | //UI线程执行(只是为了测试,长时间的加载不能放在主线程) 39 | -(void)mainQueue{ 40 | dispatch_async(dispatch_get_main_queue(), ^{ 41 | [self loadImageSource:imgUrl1]; 42 | }); 43 | } 44 | 45 | //一次性执行(常用来写单例) 46 | -(void)dispatchOnce{ 47 | static dispatch_once_t onceToken; 48 | dispatch_once(&onceToken, ^{ 49 | [self loadImageSource:imgUrl1]; 50 | }); 51 | } 52 | 53 | //并发地执行循环迭代 54 | -(void)dispatchApply{ 55 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 56 | size_t count = 10; 57 | dispatch_apply(count, queue, ^(size_t i) { 58 | NSLog(@"循环执行第%li次",i); 59 | //[self loadImageSource:imgUrl1]; 60 | }); 61 | } 62 | 63 | //后台执行:加载两张图片 64 | -(void)globalQueue2{ 65 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 66 | 67 | UIImage *image1 = [self loadImage:imgUrl1]; 68 | UIImage *image2 = [self loadImage:imgUrl2]; 69 | 70 | dispatch_async(dispatch_get_main_queue(), ^{ 71 | self.imageview1.image = image1; 72 | self.imageView2.image = image2; 73 | }); 74 | }); 75 | } 76 | 77 | //并发线程组 78 | -(void)dispatchGroup{ 79 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 80 | 81 | 82 | dispatch_async(queue, ^{ 83 | 84 | dispatch_group_t group = dispatch_group_create(); 85 | 86 | __block UIImage *image1 = nil; 87 | __block UIImage *image2 = nil; 88 | 89 | 90 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 91 | image1 = [self loadImage:imgUrl1]; 92 | }); 93 | 94 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 95 | image2 = [self loadImage:imgUrl2]; 96 | }); 97 | 98 | 99 | dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 100 | self.imageview1.image = image1; 101 | self.imageView2.image = image2; 102 | 103 | }); 104 | }); 105 | } 106 | 107 | 108 | // 延迟执行 109 | -(void)dispatchAfter{ 110 | NSLog(@"Delay 2 seconds"); 111 | double delayInSeconds = 2.0; 112 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 113 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 114 | [self loadImageSource:imgUrl1]; 115 | }); 116 | } 117 | 118 | // 自定义dispatch_queue_t 119 | -(void)defineDispatch{ 120 | 121 | dispatch_queue_t urls_queue = dispatch_queue_create("minggo.app.com", NULL); 122 | dispatch_async(urls_queue, ^{ 123 | [self loadImageSource:imgUrl1]; 124 | }); 125 | } 126 | 127 | -(void)loadImageSource:(NSString *)url{ 128 | 129 | NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 130 | UIImage *image = [UIImage imageWithData:imgData]; 131 | if (imgData!=nil) { 132 | [self performSelectorOnMainThread:@selector(refreshImageView1:) withObject:image waitUntilDone:YES]; 133 | }else{ 134 | NSLog(@"there no image data"); 135 | } 136 | 137 | } 138 | 139 | -(void)refreshImageView1:(UIImage *)image{ 140 | [self.loadingLb setHidden:YES]; 141 | [self.imageview1 setImage:image]; 142 | } 143 | 144 | -(UIImage *)loadImage:(NSString *)url{ 145 | 146 | NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 147 | UIImage *image = [UIImage imageWithData:imgData]; 148 | if (image!=nil) { 149 | return image; 150 | }else{ 151 | NSLog(@"there no image data"); 152 | return image; 153 | } 154 | 155 | } 156 | 157 | - (IBAction)onClick:(id)sender { 158 | [self.loadingLb setHidden:NO]; 159 | 160 | [self.imageview1 setImage:nil]; 161 | [self.imageView2 setImage:nil]; 162 | 163 | int index = (int)((UIButton *)sender).tag; 164 | 165 | switch (index) { 166 | case 1: 167 | [self globalQueue]; 168 | break; 169 | case 2: 170 | [self.view makeToast:@"Mybe app stop for main thread"]; 171 | [self mainQueue]; 172 | break; 173 | case 3: 174 | [self.view makeToast:@"Mybe app stop for main thread"]; 175 | [self dispatchOnce]; 176 | break; 177 | case 4: 178 | [self.view makeToast:@"Look at your Xcode"]; 179 | [self dispatchApply]; 180 | break; 181 | case 5: 182 | [self globalQueue2]; 183 | break; 184 | case 6: 185 | [self dispatchGroup]; 186 | break; 187 | case 7: 188 | [self dispatchAfter]; 189 | break; 190 | case 8: 191 | [self defineDispatch]; 192 | break; 193 | 194 | default: 195 | break; 196 | } 197 | } 198 | @end 199 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/LoadImageOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoadImageOperation.h 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol LoadImageDelegate 13 | 14 | -(void)loadImageFinish:(UIImage *) image; 15 | 16 | @end 17 | 18 | @interface LoadImageOperation : NSOperation 19 | 20 | @property(nonatomic,copy) NSString *imgUrl; 21 | @property(nonatomic,retain) id loadDelegate; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/LoadImageOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // LoadImageOperation.m 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import "LoadImageOperation.h" 10 | 11 | @implementation LoadImageOperation 12 | 13 | - (void)main { 14 | 15 | if (self.isCancelled) return; 16 | 17 | NSURL *url = [NSURL URLWithString:self.imgUrl]; 18 | NSData *imageData = [NSData dataWithContentsOfURL:url]; 19 | 20 | if (self.isCancelled) { 21 | url = nil; 22 | imageData = nil; 23 | return; 24 | } 25 | 26 | UIImage *image = [UIImage imageWithData:imageData]; 27 | 28 | if (self.isCancelled) { 29 | image = nil; 30 | return; 31 | } 32 | 33 | if (self.loadDelegate!=nil&&[self.loadDelegate respondsToSelector:@selector(loadImageFinish:)]) { 34 | 35 | [(NSObject *)self.loadDelegate performSelectorOnMainThread:@selector(loadImageFinish:) withObject:image waitUntilDone:NO]; 36 | } 37 | 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/NSOperationLoadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSOperationLoadViewController.h 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSOperationLoadViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/NSOperationLoadViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSOperationLoadViewController.m 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import "NSOperationLoadViewController.h" 10 | #import "LoadImageOperation.h" 11 | 12 | @interface NSOperationLoadViewController() 13 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 14 | @property (weak, nonatomic) IBOutlet UILabel *loadingLb; 15 | - (IBAction)load:(id)sender; 16 | 17 | @end 18 | 19 | @implementation NSOperationLoadViewController{ 20 | NSString *imgUrl; 21 | } 22 | 23 | -(void)viewDidLoad{ 24 | [super viewDidLoad]; 25 | imgUrl = @"https://d262ilb51hltx0.cloudfront.net/fit/t/880/264/1*kE8-X3OjeiiSPQFyhL2Tdg.jpeg"; 26 | 27 | } 28 | 29 | //使用子类NSInvocationOperation 30 | -(void)useInvocationOperation{ 31 | NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl]; 32 | //[invocationOperation start];//直接会在当前线程主线程执行 33 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 34 | [queue addOperation:invocationOperation]; 35 | 36 | } 37 | 38 | //使用子类NSBlockOperation 39 | -(void)useBlockOperation{ 40 | 41 | NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ 42 | [self loadImageSource:imgUrl]; 43 | }]; 44 | 45 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 46 | [queue addOperation:blockOperation]; 47 | 48 | } 49 | //使用继承NSOperation 50 | -(void)useSubclassOperation{ 51 | 52 | LoadImageOperation *imageOperation = [LoadImageOperation new]; 53 | imageOperation.loadDelegate = self; 54 | imageOperation.imgUrl = imgUrl; 55 | 56 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 57 | [queue addOperation:imageOperation]; 58 | } 59 | 60 | -(void)loadImageSource:(NSString *)url{ 61 | 62 | NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 63 | UIImage *image = [UIImage imageWithData:imgData]; 64 | if (imgData!=nil) { 65 | [self performSelectorOnMainThread:@selector(refreshImageView1:) withObject:image waitUntilDone:YES]; 66 | }else{ 67 | NSLog(@"there no image data"); 68 | } 69 | 70 | } 71 | 72 | -(void)refreshImageView1:(UIImage *)image{ 73 | [self.loadingLb setHidden:YES]; 74 | [self.imageView setImage:image]; 75 | } 76 | 77 | -(void) loadImageFinish:(UIImage *)image{ 78 | [self.loadingLb setHidden:YES]; 79 | [self.imageView setImage:image]; 80 | } 81 | 82 | - (IBAction)load:(id)sender { 83 | 84 | [self.loadingLb setHidden:NO]; 85 | 86 | [self.imageView setImage:nil]; 87 | 88 | int index = (int)((UIButton *)sender).tag; 89 | 90 | switch (index) { 91 | case 1: 92 | [self useInvocationOperation]; 93 | break; 94 | case 2: 95 | [self useBlockOperation]; 96 | break; 97 | case 3: 98 | [self useSubclassOperation]; 99 | break; 100 | 101 | default: 102 | break; 103 | } 104 | } 105 | @end 106 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/NSThreadLoadViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSThreadLoadViewController.h 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSThreadLoadViewController : UIViewController 12 | 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/NSThreadLoadViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSThreadLoadViewController.m 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/26. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import "NSThreadLoadViewController.h" 10 | 11 | @interface NSThreadLoadViewController () 12 | - (IBAction)load:(id)sender; 13 | @property (weak, nonatomic) IBOutlet UIImageView *imageView; 14 | @property (weak, nonatomic) IBOutlet UILabel *loadingLb; 15 | 16 | @end 17 | 18 | @implementation NSThreadLoadViewController{ 19 | NSString *imgUrl; 20 | } 21 | 22 | 23 | -(void)viewDidLoad{ 24 | [super viewDidLoad]; 25 | imgUrl = @"https://d262ilb51hltx0.cloudfront.net/fit/t/880/264/1*zF0J7XHubBjojgJdYRS0FA.jpeg"; 26 | } 27 | 28 | //动态创建线程 29 | -(void)dynamicCreateThread{ 30 | 31 | NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl]; 32 | thread.threadPriority = 1;// 设置线程的优先级(0.0 - 1.0,1.0最高级) 33 | [thread start]; 34 | } 35 | 36 | //静态创建线程 37 | -(void)staticCreateThread{ 38 | 39 | [NSThread detachNewThreadSelector:@selector(loadImageSource:) toTarget:self withObject:imgUrl]; 40 | 41 | } 42 | 43 | //隐式创建线程 44 | -(void)implicitCreateThread{ 45 | 46 | [self performSelectorInBackground:@selector(loadImageSource:) withObject:imgUrl]; 47 | } 48 | 49 | -(void)loadImageSource:(NSString *)url{ 50 | 51 | 52 | 53 | NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 54 | UIImage *image = [UIImage imageWithData:imgData]; 55 | if (imgData!=nil) { 56 | [self performSelectorOnMainThread:@selector(refreshImageView:) withObject:image waitUntilDone:YES]; 57 | }else{ 58 | NSLog(@"there no image data"); 59 | } 60 | 61 | } 62 | 63 | -(void)refreshImageView:(UIImage *)image{ 64 | [self.loadingLb setHidden:YES]; 65 | [self.imageView setImage:image]; 66 | } 67 | 68 | - (IBAction)load:(id)sender { 69 | [self.loadingLb setHidden:NO]; 70 | [self.imageView setImage:nil]; 71 | int index = (int)((UIButton *)sender).tag; 72 | 73 | switch (index) { 74 | case 1: 75 | [self dynamicCreateThread]; 76 | break; 77 | case 2: 78 | [self staticCreateThread]; 79 | break; 80 | case 3: 81 | [self implicitCreateThread]; 82 | break; 83 | default: 84 | break; 85 | } 86 | 87 | } 88 | 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/25. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/25. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | 20 | // Do any additional setup after loading the view, typically from a nib. 21 | } 22 | 23 | - (void)didReceiveMemoryWarning { 24 | [super didReceiveMemoryWarning]; 25 | // Dispose of any resources that can be recreated. 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThread/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MutipleThread 4 | // 5 | // Created by minggo on 16/1/25. 6 | // Copyright © 2016年 minggo. 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 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThreadTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThreadTests/MutipleThreadTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MutipleThreadTests.m 3 | // MutipleThreadTests 4 | // 5 | // Created by minggo on 16/1/25. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MutipleThreadTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation MutipleThreadTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThreadUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /MutipleThread/MutipleThreadUITests/MutipleThreadUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MutipleThreadUITests.m 3 | // MutipleThreadUITests 4 | // 5 | // Created by minggo on 16/1/25. 6 | // Copyright © 2016年 minggo. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MutipleThreadUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation MutipleThreadUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /MutipleThread/Toast/UIView+Toast.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | 3 | UIView+Toast.h 4 | Toast 5 | 6 | Copyright (c) 2014 Charles Scalesse. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included 17 | in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | ***************************************************************************/ 28 | 29 | #import 30 | 31 | extern NSString * const CSToastPositionTop; 32 | extern NSString * const CSToastPositionCenter; 33 | extern NSString * const CSToastPositionBottom; 34 | 35 | @interface UIView (Toast) 36 | 37 | // each makeToast method creates a view and displays it as toast 38 | - (void)makeToast:(NSString *)message; 39 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position; 40 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position image:(UIImage *)image; 41 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title; 42 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)interval position:(id)position title:(NSString *)title image:(UIImage *)image; 43 | 44 | // displays toast with an activity spinner 45 | - (void)makeToastActivity; 46 | - (void)makeToastActivity:(id)position; 47 | - (void)hideToastActivity; 48 | 49 | // the showToast methods display any view as toast 50 | - (void)showToast:(UIView *)toast; 51 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)interval position:(id)point; 52 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)interval position:(id)point 53 | tapCallback:(void(^)(void))tapCallback; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /MutipleThread/Toast/UIView+Toast.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Toast.m 3 | // Toast 4 | // 5 | // Copyright 2014 Charles Scalesse. 6 | // 7 | 8 | 9 | #import "UIView+Toast.h" 10 | #import 11 | #import 12 | 13 | /* 14 | * CONFIGURE THESE VALUES TO ADJUST LOOK & FEEL, 15 | * DISPLAY DURATION, ETC. 16 | */ 17 | 18 | // general appearance 19 | static const CGFloat CSToastMaxWidth = 0.8; // 80% of parent view width 20 | static const CGFloat CSToastMaxHeight = 0.8; // 80% of parent view height 21 | static const CGFloat CSToastHorizontalPadding = 10.0; 22 | static const CGFloat CSToastVerticalPadding = 8.0; 23 | static const CGFloat CSToastCornerRadius = 5.0; 24 | static const CGFloat CSToastOpacity = 0.8; 25 | static const CGFloat CSToastFontSize = 16.0; 26 | static const CGFloat CSToastMaxTitleLines = 0; 27 | static const CGFloat CSToastMaxMessageLines = 0; 28 | static const NSTimeInterval CSToastFadeDuration = 0.2; 29 | 30 | // shadow appearance 31 | static const CGFloat CSToastShadowOpacity = 0.4; 32 | static const CGFloat CSToastShadowRadius = 6.0; 33 | static const CGSize CSToastShadowOffset = { 1.0, 1.0 }; 34 | static const BOOL CSToastDisplayShadow = YES; 35 | 36 | // display duration 37 | static const NSTimeInterval CSToastDefaultDuration = 3.0; 38 | 39 | // image view size 40 | static const CGFloat CSToastImageViewWidth = 80.0; 41 | static const CGFloat CSToastImageViewHeight = 80.0; 42 | 43 | // activity 44 | static const CGFloat CSToastActivityWidth = 100.0; 45 | static const CGFloat CSToastActivityHeight = 100.0; 46 | static const NSString * CSToastActivityDefaultPosition = @"center"; 47 | 48 | // interaction 49 | static const BOOL CSToastHidesOnTap = YES; // excludes activity views 50 | 51 | // associative reference keys 52 | static const NSString * CSToastTimerKey = @"CSToastTimerKey"; 53 | static const NSString * CSToastActivityViewKey = @"CSToastActivityViewKey"; 54 | static const NSString * CSToastTapCallbackKey = @"CSToastTapCallbackKey"; 55 | 56 | // positions 57 | NSString * const CSToastPositionTop = @"top"; 58 | NSString * const CSToastPositionCenter = @"center"; 59 | NSString * const CSToastPositionBottom = @"bottom"; 60 | 61 | @interface UIView (ToastPrivate) 62 | 63 | - (void)hideToast:(UIView *)toast; 64 | - (void)toastTimerDidFinish:(NSTimer *)timer; 65 | - (void)handleToastTapped:(UITapGestureRecognizer *)recognizer; 66 | - (CGPoint)centerPointForPosition:(id)position withToast:(UIView *)toast; 67 | - (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image; 68 | - (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode; 69 | 70 | @end 71 | 72 | 73 | @implementation UIView (Toast) 74 | 75 | #pragma mark - Toast Methods 76 | 77 | - (void)makeToast:(NSString *)message { 78 | [self makeToast:message duration:CSToastDefaultDuration position:nil]; 79 | } 80 | 81 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position { 82 | UIView *toast = [self viewForMessage:message title:nil image:nil]; 83 | [self showToast:toast duration:duration position:position]; 84 | } 85 | 86 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title { 87 | UIView *toast = [self viewForMessage:message title:title image:nil]; 88 | [self showToast:toast duration:duration position:position]; 89 | } 90 | 91 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position image:(UIImage *)image { 92 | UIView *toast = [self viewForMessage:message title:nil image:image]; 93 | [self showToast:toast duration:duration position:position]; 94 | } 95 | 96 | - (void)makeToast:(NSString *)message duration:(NSTimeInterval)duration position:(id)position title:(NSString *)title image:(UIImage *)image { 97 | UIView *toast = [self viewForMessage:message title:title image:image]; 98 | [self showToast:toast duration:duration position:position]; 99 | } 100 | 101 | - (void)showToast:(UIView *)toast { 102 | [self showToast:toast duration:CSToastDefaultDuration position:nil]; 103 | } 104 | 105 | 106 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position { 107 | [self showToast:toast duration:duration position:position tapCallback:nil]; 108 | 109 | } 110 | 111 | 112 | - (void)showToast:(UIView *)toast duration:(NSTimeInterval)duration position:(id)position 113 | tapCallback:(void(^)(void))tapCallback 114 | { 115 | toast.center = [self centerPointForPosition:position withToast:toast]; 116 | toast.alpha = 0.0; 117 | 118 | if (CSToastHidesOnTap) { 119 | UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:toast action:@selector(handleToastTapped:)]; 120 | [toast addGestureRecognizer:recognizer]; 121 | toast.userInteractionEnabled = YES; 122 | toast.exclusiveTouch = YES; 123 | } 124 | 125 | [self addSubview:toast]; 126 | 127 | [UIView animateWithDuration:CSToastFadeDuration 128 | delay:0.0 129 | options:(UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionAllowUserInteraction) 130 | animations:^{ 131 | toast.alpha = 1.0; 132 | } completion:^(BOOL finished) { 133 | NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:duration target:self selector:@selector(toastTimerDidFinish:) userInfo:toast repeats:NO]; 134 | // associate the timer with the toast view 135 | objc_setAssociatedObject (toast, &CSToastTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 136 | objc_setAssociatedObject (toast, &CSToastTapCallbackKey, tapCallback, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 137 | }]; 138 | } 139 | 140 | 141 | - (void)hideToast:(UIView *)toast { 142 | [UIView animateWithDuration:CSToastFadeDuration 143 | delay:0.0 144 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 145 | animations:^{ 146 | toast.alpha = 0.0; 147 | } completion:^(BOOL finished) { 148 | [toast removeFromSuperview]; 149 | }]; 150 | } 151 | 152 | #pragma mark - Events 153 | 154 | - (void)toastTimerDidFinish:(NSTimer *)timer { 155 | [self hideToast:(UIView *)timer.userInfo]; 156 | } 157 | 158 | - (void)handleToastTapped:(UITapGestureRecognizer *)recognizer { 159 | NSTimer *timer = (NSTimer *)objc_getAssociatedObject(self, &CSToastTimerKey); 160 | [timer invalidate]; 161 | 162 | void (^callback)(void) = objc_getAssociatedObject(self, &CSToastTapCallbackKey); 163 | if (callback) { 164 | callback(); 165 | } 166 | [self hideToast:recognizer.view]; 167 | } 168 | 169 | #pragma mark - Toast Activity Methods 170 | 171 | - (void)makeToastActivity { 172 | [self makeToastActivity:CSToastActivityDefaultPosition]; 173 | } 174 | 175 | - (void)makeToastActivity:(id)position { 176 | // sanity 177 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 178 | if (existingActivityView != nil) return; 179 | 180 | UIView *activityView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CSToastActivityWidth, CSToastActivityHeight)]; 181 | activityView.center = [self centerPointForPosition:position withToast:activityView]; 182 | activityView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity]; 183 | activityView.alpha = 0.0; 184 | activityView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 185 | activityView.layer.cornerRadius = CSToastCornerRadius; 186 | 187 | if (CSToastDisplayShadow) { 188 | activityView.layer.shadowColor = [UIColor blackColor].CGColor; 189 | activityView.layer.shadowOpacity = CSToastShadowOpacity; 190 | activityView.layer.shadowRadius = CSToastShadowRadius; 191 | activityView.layer.shadowOffset = CSToastShadowOffset; 192 | } 193 | 194 | UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; 195 | activityIndicatorView.center = CGPointMake(activityView.bounds.size.width / 2, activityView.bounds.size.height / 2); 196 | [activityView addSubview:activityIndicatorView]; 197 | [activityIndicatorView startAnimating]; 198 | 199 | // associate the activity view with self 200 | objc_setAssociatedObject (self, &CSToastActivityViewKey, activityView, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 201 | 202 | [self addSubview:activityView]; 203 | 204 | [UIView animateWithDuration:CSToastFadeDuration 205 | delay:0.0 206 | options:UIViewAnimationOptionCurveEaseOut 207 | animations:^{ 208 | activityView.alpha = 1.0; 209 | } completion:nil]; 210 | } 211 | 212 | - (void)hideToastActivity { 213 | UIView *existingActivityView = (UIView *)objc_getAssociatedObject(self, &CSToastActivityViewKey); 214 | if (existingActivityView != nil) { 215 | [UIView animateWithDuration:CSToastFadeDuration 216 | delay:0.0 217 | options:(UIViewAnimationOptionCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState) 218 | animations:^{ 219 | existingActivityView.alpha = 0.0; 220 | } completion:^(BOOL finished) { 221 | [existingActivityView removeFromSuperview]; 222 | objc_setAssociatedObject (self, &CSToastActivityViewKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 223 | }]; 224 | } 225 | } 226 | 227 | #pragma mark - Helpers 228 | 229 | - (CGPoint)centerPointForPosition:(id)point withToast:(UIView *)toast { 230 | if([point isKindOfClass:[NSString class]]) { 231 | if([point caseInsensitiveCompare:CSToastPositionTop] == NSOrderedSame) { 232 | return CGPointMake(self.bounds.size.width/2, (toast.frame.size.height / 2) + CSToastVerticalPadding); 233 | } else if([point caseInsensitiveCompare:CSToastPositionCenter] == NSOrderedSame) { 234 | return CGPointMake(self.bounds.size.width / 2, self.bounds.size.height / 2); 235 | } 236 | } else if ([point isKindOfClass:[NSValue class]]) { 237 | return [point CGPointValue]; 238 | } 239 | 240 | // default to bottom 241 | return CGPointMake(self.bounds.size.width/2, (self.bounds.size.height - (toast.frame.size.height / 2)) - CSToastVerticalPadding*7); 242 | } 243 | 244 | - (CGSize)sizeForString:(NSString *)string font:(UIFont *)font constrainedToSize:(CGSize)constrainedSize lineBreakMode:(NSLineBreakMode)lineBreakMode { 245 | if ([string respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)]) { 246 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 247 | paragraphStyle.lineBreakMode = lineBreakMode; 248 | NSDictionary *attributes = @{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}; 249 | CGRect boundingRect = [string boundingRectWithSize:constrainedSize options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil]; 250 | return CGSizeMake(ceilf(boundingRect.size.width), ceilf(boundingRect.size.height)); 251 | } 252 | 253 | #pragma clang diagnostic push 254 | #pragma clang diagnostic ignored "-Wdeprecated-declarations" 255 | return [string sizeWithFont:font constrainedToSize:constrainedSize lineBreakMode:lineBreakMode]; 256 | #pragma clang diagnostic pop 257 | } 258 | 259 | - (UIView *)viewForMessage:(NSString *)message title:(NSString *)title image:(UIImage *)image { 260 | // sanity 261 | if((message == nil) && (title == nil) && (image == nil)) return nil; 262 | 263 | // dynamically build a toast view with any combination of message, title, & image. 264 | UILabel *messageLabel = nil; 265 | UILabel *titleLabel = nil; 266 | UIImageView *imageView = nil; 267 | 268 | // create the parent view 269 | UIView *wrapperView = [[UIView alloc] init]; 270 | wrapperView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin); 271 | wrapperView.layer.cornerRadius = CSToastCornerRadius; 272 | 273 | if (CSToastDisplayShadow) { 274 | wrapperView.layer.shadowColor = [UIColor blackColor].CGColor; 275 | wrapperView.layer.shadowOpacity = CSToastShadowOpacity; 276 | wrapperView.layer.shadowRadius = CSToastShadowRadius; 277 | wrapperView.layer.shadowOffset = CSToastShadowOffset; 278 | } 279 | 280 | wrapperView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:CSToastOpacity]; 281 | 282 | if(image != nil) { 283 | imageView = [[UIImageView alloc] initWithImage:image]; 284 | imageView.contentMode = UIViewContentModeScaleAspectFit; 285 | imageView.frame = CGRectMake(CSToastHorizontalPadding, CSToastVerticalPadding, CSToastImageViewWidth, CSToastImageViewHeight); 286 | } 287 | 288 | CGFloat imageWidth, imageHeight, imageLeft; 289 | 290 | // the imageView frame values will be used to size & position the other views 291 | if(imageView != nil) { 292 | imageWidth = imageView.bounds.size.width; 293 | imageHeight = imageView.bounds.size.height; 294 | imageLeft = CSToastHorizontalPadding; 295 | } else { 296 | imageWidth = imageHeight = imageLeft = 0.0; 297 | } 298 | 299 | if (title != nil) { 300 | titleLabel = [[UILabel alloc] init]; 301 | titleLabel.numberOfLines = CSToastMaxTitleLines; 302 | titleLabel.font = [UIFont boldSystemFontOfSize:CSToastFontSize]; 303 | titleLabel.textAlignment = NSTextAlignmentLeft; 304 | titleLabel.lineBreakMode = NSLineBreakByWordWrapping; 305 | titleLabel.textColor = [UIColor whiteColor]; 306 | titleLabel.backgroundColor = [UIColor clearColor]; 307 | titleLabel.alpha = 1.0; 308 | titleLabel.text = title; 309 | 310 | // size the title label according to the length of the text 311 | CGSize maxSizeTitle = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight); 312 | CGSize expectedSizeTitle = [self sizeForString:title font:titleLabel.font constrainedToSize:maxSizeTitle lineBreakMode:titleLabel.lineBreakMode]; 313 | titleLabel.frame = CGRectMake(0.0, 0.0, expectedSizeTitle.width, expectedSizeTitle.height); 314 | } 315 | 316 | if (message != nil) { 317 | messageLabel = [[UILabel alloc] init]; 318 | messageLabel.numberOfLines = CSToastMaxMessageLines; 319 | messageLabel.font = [UIFont systemFontOfSize:CSToastFontSize]; 320 | messageLabel.lineBreakMode = NSLineBreakByWordWrapping; 321 | messageLabel.textColor = [UIColor whiteColor]; 322 | messageLabel.backgroundColor = [UIColor clearColor]; 323 | messageLabel.alpha = 1.0; 324 | messageLabel.text = message; 325 | 326 | // size the message label according to the length of the text 327 | CGSize maxSizeMessage = CGSizeMake((self.bounds.size.width * CSToastMaxWidth) - imageWidth, self.bounds.size.height * CSToastMaxHeight); 328 | CGSize expectedSizeMessage = [self sizeForString:message font:messageLabel.font constrainedToSize:maxSizeMessage lineBreakMode:messageLabel.lineBreakMode]; 329 | messageLabel.frame = CGRectMake(0.0, 0.0, expectedSizeMessage.width, expectedSizeMessage.height); 330 | } 331 | 332 | // titleLabel frame values 333 | CGFloat titleWidth, titleHeight, titleTop, titleLeft; 334 | 335 | if(titleLabel != nil) { 336 | titleWidth = titleLabel.bounds.size.width; 337 | titleHeight = titleLabel.bounds.size.height; 338 | titleTop = CSToastVerticalPadding; 339 | titleLeft = imageLeft + imageWidth + CSToastHorizontalPadding; 340 | } else { 341 | titleWidth = titleHeight = titleTop = titleLeft = 0.0; 342 | } 343 | 344 | // messageLabel frame values 345 | CGFloat messageWidth, messageHeight, messageLeft, messageTop; 346 | 347 | if(messageLabel != nil) { 348 | messageWidth = messageLabel.bounds.size.width; 349 | messageHeight = messageLabel.bounds.size.height; 350 | messageLeft = imageLeft + imageWidth + CSToastHorizontalPadding; 351 | messageTop = titleTop + titleHeight + CSToastVerticalPadding; 352 | } else { 353 | messageWidth = messageHeight = messageLeft = messageTop = 0.0; 354 | } 355 | 356 | CGFloat longerWidth = MAX(titleWidth, messageWidth); 357 | CGFloat longerLeft = MAX(titleLeft, messageLeft); 358 | 359 | // wrapper width uses the longerWidth or the image width, whatever is larger. same logic applies to the wrapper height 360 | CGFloat wrapperWidth = MAX((imageWidth + (CSToastHorizontalPadding * 2)), (longerLeft + longerWidth + CSToastHorizontalPadding)); 361 | CGFloat wrapperHeight = MAX((messageTop + messageHeight + CSToastVerticalPadding), (imageHeight + (CSToastVerticalPadding * 2))); 362 | 363 | wrapperView.frame = CGRectMake(0.0, 0.0, wrapperWidth, wrapperHeight); 364 | 365 | if(titleLabel != nil) { 366 | titleLabel.frame = CGRectMake(titleLeft, titleTop, titleWidth, titleHeight); 367 | [wrapperView addSubview:titleLabel]; 368 | } 369 | 370 | if(messageLabel != nil) { 371 | messageLabel.frame = CGRectMake(messageLeft, messageTop, messageWidth, messageHeight); 372 | [wrapperView addSubview:messageLabel]; 373 | } 374 | 375 | if(imageView != nil) { 376 | [wrapperView addSubview:imageView]; 377 | } 378 | 379 | return wrapperView; 380 | } 381 | 382 | @end 383 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##微信公众号: 2 | 3 | ![学习流程图](https://github.com/minggo620/iOSRuntimeLearn/blob/master/picture/gongzhonghao.jpg?raw=true) 4 | # 谈iOS多线程(NSThread、NSOperation、GCD)编程 5 | [![Support](https://img.shields.io/badge/support-iOS%207%2B%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)  6 | [![Travis](https://img.shields.io/travis/rust-lang/rust.svg)]() 7 | [![GitHub release](https://img.shields.io/github/release/qubyte/rubidium.svg)]() 8 | [![Github All Releases](https://img.shields.io/badge/download-6M Total-green.svg)](https://github.com/minggo620/iOSMutipleThread/archive/master.zip) 9 | 10 | ![文章配图](https://github.com/minggo620/iOSMutipleThread/blob/master/picture/multiplethread2.jpeg?raw=true) 11 | 12 | 一周六早上,小明处于安全考虑,去银行服务厅申请多一张银行卡作为手机消费指定数额不多的专用卡。到了银行,看到大厅坐满了人,唱K的唱K,念经的念经,呕奶的呕奶,彼起此伏,声声入耳,直赶清华大学演奏团演奏的《小苹果》,呀~!其实真实的情况是:每个人都做着椅子上低下头盯着各自的手机,小明也不例外,找了个角落,浏览起3016年的新闻。半个小时过去了,40分钟过去了,一个小时过去!小明等怒了,大喊“嘿嘿嘿,开多一条线程不可以吗!!!” 13 | #####“什么是多一条线程啊?” 14 | ![文章大纲](https://github.com/minggo620/iOSMutipleThread/blob/master/picture/multiplethread1.png?raw=true) 15 | ###一.基本概念 16 | 计算机操作系统都有的基本概念,以下概念简单方式来描述。 17 | 18 | 1. 进程: 一个具有一定独立功能的程序关于某个数据集合的一次运行活动。可以理解成一个运行中的应用程序。 19 | 2. 线程: 程序执行流的最小单元,线程是进程中的一个实体。 20 | 3. 同步: 只能在当前线程按先后顺序依次执行,不开启新线程。 21 | 4. 异步: 可以在当前线程开启多个新线程执行,可不按顺序执行。 22 | 5. 队列: 装载线程任务的队形结构。 23 | 6. 并发: 线程执行可以同时一起进行执行。 24 | 7. 串行: 线程执行只能依次逐一先后有序的执行。 25 | 26 | ***注意:*** 27 | 28 | * 一个进程可有多个线程。 29 | * 一个进程可有多个队列。 30 | * 队列可分并发队列和串行队列。 31 | 32 | ###二.iOS多线程对比 33 | #####1. NSThread 34 | 每个NSThread对象对应一个线程,真正最原始的线程。 35 | 1)优点:NSThread 轻量级最低,相对简单。 36 | 2)缺点:手动管理所有的线程活动,如生命周期、线程同步、睡眠等。 37 | #####2. NSOperation 38 | 自带线程管理的抽象类。 39 | 1)优点:自带线程周期管理,操作上可更注重自己逻辑。 40 | 2)缺点:面向对象的抽象类,只能实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。 41 | #####3. GCD 42 | Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。 43 | 1)优点:最高效,避开并发陷阱。 44 | 2)缺点:基于C实现。 45 | #####5. 选择小结 46 | 1)简单而安全的选择NSOperation实现多线程即可。 47 | 2)处理大量并发数据,又追求性能效率的选择GCD。 48 | 3)NSThread本人选择基本上是在做些小测试上使用,当然也可以基于此造个轮子。 49 | 50 | ###三.场景选择 51 | 52 | 1. **图片异步加载**。这种常见的场景是最常见也是必不可少的。异步加载图片有分成两种来说明一下。 53 | 第一种,在UI主线程开启新线程按顺序加载图片,加载完成刷新UI。 54 | 第二种,依然是在主线程开启新线程,顺序不定地加载图片,加载完成个字刷新UI。 55 | 2. **创作工具上的异步。** 这个跟上边任务调度道理,只是为了丰富描述,有助于“举一反三”效果。如下描述的是app创作小说。 56 | 场景一,app本地创作10个章节内容未成同步服务器,同时发表这10个章节产生的一系列动作,其中上传内容,获取分配章节Id,如何后台没有做处理最好方式做**异步按顺序执行。** 57 | 场景二,app本地创作列表中有3本小说为发表,同时发表创作列表中的3本小说,自然考虑**并行队列执行**发表。 58 | 59 | ###四.使用方法 60 | 第三标题场景选择内容实现先留下一个悬念。具体实现还是先熟知一下各自的API先。 61 | #####1. NSThread 62 | **1.1)三种实现开启线程方式:** 63 | ①.动态实例化 64 | 65 | NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl]; 66 | thread.threadPriority = 1;// 设置线程的优先级(0.0 - 1.0,1.0最高级) 67 | [thread start]; 68 | 69 | ②.静态实例化 70 | 71 | [NSThread detachNewThreadSelector:@selector(loadImageSource:) toTarget:self withObject:imgUrl]; 72 | 73 | ③.隐式实例化 74 | 75 | [self performSelectorInBackground:@selector(loadImageSource:) withObject:imgUrl]; 76 | 77 | 有了以上的知识点,可以试探了一下编写场景选择中的“图片加载”的基本功能了。 78 | 79 | **1.2)使用这三种方式编写代码** 80 | 81 | //动态创建线程 82 | -(void)dynamicCreateThread{ 83 | 84 | NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl]; 85 | thread.threadPriority = 1;// 设置线程的优先级(0.0 - 1.0,1.0最高级) 86 | [thread start]; 87 | } 88 | 89 | //静态创建线程 90 | -(void)staticCreateThread{ 91 | 92 | [NSThread detachNewThreadSelector:@selector(loadImageSource:) toTarget:self withObject:imgUrl]; 93 | 94 | } 95 | 96 | //隐式创建线程 97 | -(void)implicitCreateThread{ 98 | 99 | [self performSelectorInBackground:@selector(loadImageSource:) withObject:imgUrl]; 100 | } 101 | 102 | -(void)loadImageSource:(NSString *)url{ 103 | NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 104 | UIImage *image = [UIImage imageWithData:imgData]; 105 | if (imgData!=nil) { 106 | [self performSelectorOnMainThread:@selector(refreshImageView:) withObject:image waitUntilDone:YES]; 107 | }else{ 108 | NSLog(@"there no image data"); 109 | } 110 | 111 | } 112 | 113 | -(void)refreshImageView:(UIImage *)image{ 114 | [self.imageView setImage:image]; 115 | } 116 | 117 | **1.3)看先效果图** 118 | ![NSThread多线程加载效果](https://github.com/minggo620/iOSMutipleThread/blob/master/picture/mutiplethread1.gif?raw=true) 119 | 120 | **1.4)NSThread的拓展认识** 121 | ①获取当前线程 122 | 123 | NSThread *current = [NSThread currentThread]; 124 | 125 | ②获取主线程 126 | 127 | NSThread *main = [NSThread mainThread]; 128 | 129 | ③暂停当前线程 130 | 131 | [NSThread sleepForTimeInterval:2]; 132 | 133 | ④线程之间通信 134 | 135 | //在指定线程上执行操作 136 | [self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES]; 137 | //在主线程上执行操作 138 | [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; 139 | //在当前线程执行操作 140 | [self performSelector:@selector(run) withObject:nil]; 141 | 142 | 显然动态创建线程多了几行代码,其实就是那几行代码,如果重复编写数遍那是一件多么不爽的事情。首次看来静态方法创作线程和隐式创建线程显得比较方便,简洁。从知识结构来说,讲到这里应该讲述一下**线程锁**,鉴于并不常用和文章过长就不在此详细讲述,有兴趣可以自行查阅。 143 | #####2. NSOperation 144 | 主要的实现方式:结合NSOperation和NSOperationQueue实现多线程编程。 145 | 146 | * 实例化NSOperation的子类,绑定执行的操作。 147 | * 创建NSOperationQueue队列,将NSOperation实例添加进来。 148 | * 系统会自动将NSOperationQueue队列中检测取出和执行NSOperation的操作。 149 | 150 | **2.1)使用NSOperation的子类实现创作线程。** 151 | ①.NSInvocationOperation创建线程。 152 | 153 | NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl]; 154 | //[invocationOperation start];//直接会在当前线程主线程执行 155 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 156 | [queue addOperation:invocationOperation]; 157 | 158 | ②.NSBlockOperation创建线程 159 | 160 | NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ 161 | [self loadImageSource:imgUrl]; 162 | }]; 163 | 164 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 165 | [queue addOperation:blockOperation]; 166 | 167 | ③.自定义NSOperation子类实现main方法 168 | 169 | 实现main方法 170 | 171 | -(void)main { 172 | // Do somthing 173 | } 174 | 175 | * 创建线程实例并添加到队列中 176 | 177 | LoadImageOperation *imageOperation = [LoadImageOperation new]; 178 | imageOperation.loadDelegate = self; 179 | imageOperation.imgUrl = imgUrl; 180 | 181 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 182 | [queue addOperation:imageOperation]; 183 | 184 | **2.2)使用这三种方式编写代码** 185 | 186 | 创建各个实例并添加到队列表当中 187 | 188 | //使用子类NSInvocationOperation 189 | -(void)useInvocationOperation{ 190 | NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imgUrl]; 191 | //[invocationOperation start];//直接会在当前线程主线程执行 192 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 193 | [queue addOperation:invocationOperation]; 194 | 195 | } 196 | 197 | //使用子类NSBlockOperation 198 | -(void)useBlockOperation{ 199 | 200 | NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ 201 | [self loadImageSource:imgUrl]; 202 | }]; 203 | 204 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 205 | [queue addOperation:blockOperation]; 206 | 207 | } 208 | //使用继承NSOperation 209 | -(void)useSubclassOperation{ 210 | 211 | LoadImageOperation *imageOperation = [LoadImageOperation new]; 212 | imageOperation.loadDelegate = self; 213 | imageOperation.imgUrl = imgUrl; 214 | 215 | NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 216 | [queue addOperation:imageOperation]; 217 | } 218 | 219 | -(void)loadImageSource:(NSString *)url{ 220 | 221 | NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 222 | UIImage *image = [UIImage imageWithData:imgData]; 223 | if (imgData!=nil) { 224 | [self performSelectorOnMainThread:@selector(refreshImageView1:) withObject:image waitUntilDone:YES]; 225 | }else{ 226 | NSLog(@"there no image data"); 227 | } 228 | 229 | } 230 | 231 | -(void)refreshImageView1:(UIImage *)image{ 232 | [self.loadingLb setHidden:YES]; 233 | [self.imageView setImage:image]; 234 | } 235 | 236 | -(void) loadImageFinish:(UIImage *)image{ 237 | [self.loadingLb setHidden:YES]; 238 | [self.imageView setImage:image]; 239 | } 240 | 241 | 附自定义NSOperation子类main主要代码实现 242 | 243 | - (void)main { 244 | 245 | if (self.isCancelled) return; 246 | 247 | NSURL *url = [NSURL URLWithString:self.imgUrl]; 248 | NSData *imageData = [NSData dataWithContentsOfURL:url]; 249 | 250 | if (self.loadDelegate!=nil&&[self.loadDelegate respondsToSelector:@selector(loadImageFinish:)]) { 251 | 252 | [(NSObject *)self.loadDelegate performSelectorOnMainThread:@selector(loadImageFinish:) withObject:image waitUntilDone:NO]; 253 | } 254 | } 255 | 256 | **2.3)看先效果图** 257 | ![NSOperation多线程加载效果](https://github.com/minggo620/iOSMutipleThread/blob/master/picture/mutiplethread2.gif?raw=true) 258 | #####3. GCD多线程 259 | GCD是Apple开发,据说高性能的多线程解决方案。既然这样,就细说一下这个解决方案。 260 | 进过Nsthread和NSOperation的讲述和上边的基础概念,可以开始组合用起来吧。**并发队列**、**串行队列**都用起来。 261 | **3.1)分发队列种类(dispatch queue)** 262 | ①.UI主线程队列 main queue 263 | 264 | dispatch_get_main_queue() 265 | 266 | ②.并行队列global dispatch queue 267 | 268 | dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 269 | 270 | 这里的两个参数得说明一下:第一个参数用于指定优先级,分别使用DISPATCH_QUEUE_PRIORITY_HIGH和DISPATCH_QUEUE_PRIORITY_LOW两个常量来获取高和低优先级的两个queue;第二个参数目前未使用到,默认0即可 271 | 272 | ③.串行队列serial queues 273 | 274 | dispatch_queue_create("minggo.app.com", NULL); 275 | 276 | **3.2)6中多线程实现** 277 | ①后台执行线程创建 278 | 279 | dispatch_async(dispatch_get_global_queue(0, 0), ^{ 280 | [self loadImageSource:imgUrl1]; 281 | }); 282 | 283 | ②UI线程执行(只是为了测试,长时间加载内容不放在主线程) 284 | 285 | dispatch_async(dispatch_get_main_queue(), ^{ 286 | [self loadImageSource:imgUrl1]; 287 | }); 288 | ③一次性执行(常用来写单例) 289 | 290 | static dispatch_once_t onceToken; 291 | dispatch_once(&onceToken, ^{ 292 | [self loadImageSource:imgUrl1]; 293 | }); 294 | ④并发地执行循环迭代 295 | 296 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 297 | size_t count = 10; 298 | dispatch_apply(count, queue, ^(size_t i) { 299 | NSLog(@"循环执行第%li次",i); 300 | [self loadImageSource:imgUrl1]; 301 | }); 302 | ⑤延迟执行 303 | 304 | double delayInSeconds = 2.0; 305 | dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); 306 | dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ 307 | [self loadImageSource:imgUrl1]; 308 | }); 309 | ⑥自定义dispatch_queue_t 310 | 311 | dispatch_queue_t urls_queue = dispatch_queue_create("minggo.app.com", NULL); 312 | dispatch_async(urls_queue, ^{ 313 | [self loadImageSource:imgUrl1]; 314 | }); 315 | 316 | **3.3)对比多任务执行** 317 | 异步加载图片是大部分app都要问题,那么加载图片是按循序加载完之后才刷新UI呢?还是不安顺序加载UI呢?显然大部分的希望各自加载各自的图片,各自刷新。以下就是模拟这两种场景。 318 | 319 | ①先后执行,加载两张图片为例 320 | 321 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 322 | 323 | UIImage *image1 = [self loadImage:imgUrl1]; 324 | UIImage *image2 = [self loadImage:imgUrl2]; 325 | 326 | dispatch_async(dispatch_get_main_queue(), ^{ 327 | self.imageview1.image = image1; 328 | self.imageView2.image = image2; 329 | }); 330 | }); 331 | 332 | ②并行队列执行,也是以加载两张图片为例 333 | 334 | dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 335 | 336 | 337 | dispatch_async(queue, ^{ 338 | 339 | dispatch_group_t group = dispatch_group_create(); 340 | 341 | __block UIImage *image1 = nil; 342 | __block UIImage *image2 = nil; 343 | 344 | 345 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 346 | image1 = [self loadImage:imgUrl1]; 347 | }); 348 | 349 | dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 350 | image2 = [self loadImage:imgUrl2]; 351 | }); 352 | 353 | 354 | dispatch_group_notify(group, dispatch_get_main_queue(), ^{ 355 | self.imageview1.image = image1; 356 | self.imageView2.image = image2; 357 | 358 | }); 359 | }); 360 | ①中等到两张图片加载完成后一起刷新,②就是典型的异步并行的例子,不需要理会各自图片加载的先后问题,完成加载图片刷新UI即可。从加载图片中来说,第①种不太合适使用,但是对于在上边场景选择的时候的创作工具来说有很大的好处,首先得异步进行,然后异步中有得按顺序执行几个任务,比如上传章节内容。因此,我们可以灵活考虑使用这两多线程任务执行方式,实现各种场景。 361 | **3.4)编码实现** 362 | 以上3.3的内容99%代码一样,就不提供一个稍微整体的代码了。看看下边的效果图吧。 363 | **3.5)效果图如下** 364 | ![GCD多线程加载效果](https://github.com/minggo620/iOSMutipleThread/blob/master/picture/mutiplethread3.gif?raw=true) 365 | ###五.源码地址 366 | ##### ***[https://github.com/minggo620/iOSMutipleThread.git](https://github.com/minggo620/iOSMutipleThread.git)*** 367 | 368 | ##### 如果小明这么跟银行柜台的MM讲多线程,会不会。。。“给我滚出去~~”。 -------------------------------------------------------------------------------- /picture/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minggo620/iOSMutipleThread/3ee36aaf69dc5b2400fbb17ffdf4da76a47c84c0/picture/.DS_Store -------------------------------------------------------------------------------- /picture/multiplethread1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minggo620/iOSMutipleThread/3ee36aaf69dc5b2400fbb17ffdf4da76a47c84c0/picture/multiplethread1.png -------------------------------------------------------------------------------- /picture/multiplethread2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minggo620/iOSMutipleThread/3ee36aaf69dc5b2400fbb17ffdf4da76a47c84c0/picture/multiplethread2.jpeg -------------------------------------------------------------------------------- /picture/mutiplethread1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minggo620/iOSMutipleThread/3ee36aaf69dc5b2400fbb17ffdf4da76a47c84c0/picture/mutiplethread1.gif -------------------------------------------------------------------------------- /picture/mutiplethread2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minggo620/iOSMutipleThread/3ee36aaf69dc5b2400fbb17ffdf4da76a47c84c0/picture/mutiplethread2.gif -------------------------------------------------------------------------------- /picture/mutiplethread3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minggo620/iOSMutipleThread/3ee36aaf69dc5b2400fbb17ffdf4da76a47c84c0/picture/mutiplethread3.gif --------------------------------------------------------------------------------